JS-in-HTML Testing
liaohch3/claude-tap
Tests JavaScript embedded in an HTML file in two layers: pytest checks of the logic ported to Python, and Playwright runs in a real browser for the DOM.
Mandatory testing conventions including AAA pattern, test naming, assertions, and mocks.
$ npx skills add macalbert/envilder --skill python-testing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install macalbert/envilder python-testing --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/python-testing .claude/skills/python-testing && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .claude/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/macalbert/envilder/tree/main/.github/skills/python-testingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add macalbert/envilder --skill python-testing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install macalbert/envilder python-testing --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/python-testing .agents/skills/python-testing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .agents/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add macalbert/envilder --skill python-testing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install macalbert/envilder python-testing --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/python-testing .cursor/skills/python-testing && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .cursor/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/macalbert/envilder.git --path .github/skills/python-testing--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add macalbert/envilder --skill python-testing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install macalbert/envilder python-testing --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/python-testing .gemini/skills/python-testing && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .gemini/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install macalbert/envilder python-testingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add macalbert/envilder --skill python-testing -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/python-testing .github/skills/python-testing && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .github/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add macalbert/envilder --skill python-testing -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install macalbert/envilder python-testing --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/macalbert/envilder.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/python-testing .opencode/skills/python-testing && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "python-testing" agent skill from https://github.com/macalbert/envilder/tree/main/.github/skills/python-testing into .opencode/skills/python-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-testing", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
python-testingMandatory testing conventions including AAA pattern, test naming, assertions, and mocks.
Python Testing is an agent skill from macalbert/envilder. Mandatory testing conventions including AAA pattern, test naming, assertions, and mocks. Use for unit, integration, or E2E tests with pytest, unittest, pytest-asyncio, or Playwright.
Its SKILL.md is about 3.1k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `examples.md` and `reference.md`).
It sits in Testing & QA, covering Unit testing, End-to-end testing and Technical documentation. It works with pytest, Python and Playwright. The repository describes itself as: One secret mapping for local dev, CI/CD, and runtime. Envilder resolves cloud secrets from your own vaults without SaaS middlemen, duplicated config, or .env drift. The licence is MIT.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit b6a0327. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
pytestFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Python Testing loads about 3.1k tokens when it runs. Until then it costs about 49 tokens; SKILL.md has 614 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from macalbert/envilder at commit b6a0327, republished under its MIT licence (© macalbert). 614 words, ~3,054 tokens.
.claude/skills/python-testing/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.This skill defines the MANDATORY testing conventions for Python projects. These are rules, not guidelines.
# Arrange, # Act, # Assert)Should_X_When_Y already documents the intentException: SDK public API: Code under src/sdks/*/ that is consumed by
external developers (e.g. facade classes, public entry points) SHOULD have
docstrings with usage examples. External users rely on IDE tooltips and help().
This exception does not apply to tests or internal helpers.
class TestUserService:
def Should_CreateUser_When_Valid(self) -> None:
# This creates a user # NO explanatory comments
user = UserFactory.build()class TestUserService:
def Should_CreateUser_When_Valid(self) -> None:
# Arrange
user = UserFactory.build()
# Act
actual = self._sut.create(user)
# Assert
assert actual is not NoneALL tests MUST follow the AAA pattern, separated by inline comments.
# Arrange, # Act, # Assert) appears AT MOST ONCE per test: if you need two actions
or two asserts, write two testsif, switch, or conditional logic inside Arrange, Act, or Assert blockstry/catch/finally inside tests: use pytest fixtures with yield for teardown/cleanup# Act & Assert combined blocks: Act and Assert are ALWAYS separatelambda before assertingdef Should_CreateGroup_When_RequestIsValid(
group_repository: Mock,
sut: GroupService
) -> None:
# Arrange
request = CreateGroupRequest(
name="Test Group",
type=GroupType.RECURRING
)
group_repository.get_group_by_id.return_value = None
# Act
actual = sut.create_group(request)
# Assert
assert actual is not None
assert actual.id is not None
assert actual.name == "Test Group"
group_repository.add_group.assert_called_once()
group_repository.save.assert_called_once()Test names MUST follow exactly:
Should_{ExpectedBehavior}_When_{Condition}When clausetest_ prefix is FORBIDDEN[tool.pytest.ini_options]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["Should_*"]If this config is missing → tests are wrong.
| Purpose | Name |
|---|---|
| Subject under test | sut |
| Expected value | expected |
| Actual result | actual |
No creativity allowed here.
@pytest.mark.asyncio
async def Should_ReturnUser_When_UserExists(
user_repository: AsyncMock,
sut: GetUserHandler
) -> None:
# Arrange
expected = UserMother.create()
user_repository.get_by_id.return_value = expected
# Act
actual = await sut.handle(expected.id)
# Assert
assert actual == expected
user_repository.get_by_id.assert_awaited_once()Act and Assert MUST be separate. Extract the action into a lambda in the Act phase.
def Should_RaiseValueError_When_NameIsEmpty(sut: GroupService) -> None:
# Arrange
request = CreateGroupRequest(name="", type=GroupType.RECURRING)
# Act
action = lambda: sut.create_group(request)
# Assert
with pytest.raises(ValueError, match="Group name is required"):
action()def Should_RaiseValueError_When_NameIsEmpty(sut: GroupService) -> None:
# Arrange
request = CreateGroupRequest(name="", type=GroupType.RECURRING)
# Act & Assert ← NEVER DO THIS
with pytest.raises(ValueError, match="Group name is required"):
sut.create_group(request)Never use try/finally in tests. Use pytest fixtures with yield for cleanup.
@pytest.fixture()
def env_cleanup() -> Generator[list[str], None, None]:
keys: list[str] = []
yield keys
for key in keys:
os.environ.pop(key, None)
class TestEnvilderClient:
def Should_SetEnvVars_When_InjectCalled(
self, env_cleanup: list[str]
) -> None:
# Arrange
secrets = {"MY_TOKEN": "token-123"}
env_cleanup.extend(secrets.keys())
# Act
EnvilderClient.inject_into_environment(secrets)
# Assert
assert os.environ["MY_TOKEN"] == "token-123"def Should_SetEnvVars_When_InjectCalled(self) -> None:
secrets = {"MY_TOKEN": "token-123"}
try:
EnvilderClient.inject_into_environment(secrets)
assert os.environ["MY_TOKEN"] == "token-123"
finally:
os.environ.pop("MY_TOKEN", None)If you mock something, you MUST verify it.
group_repository.add_group.assert_called_once()
group_repository.save.assert_called_once()
group_repository.delete_group.assert_not_called()Async:
repository.save.assert_awaited_once()No verification → test rejected.
Use Mother Pattern or Builder Pattern for creating test data. Both approaches are valid and recommended over inline object creation.
from dataclasses import dataclass
from uuid import UUID, uuid4
from typing import Optional
@dataclass
class Group:
id: UUID
name: str
type: GroupType
class GroupMother:
@staticmethod
def create(
id: Optional[UUID] = None,
name: Optional[str] = None,
type: Optional[GroupType] = None,
) -> Group:
return Group(
id=id or uuid4(),
name=name or "Test Group",
type=type or GroupType.RECURRING,
)Usage:
# Arrange
expected = GroupMother.create(name="Custom Name")Builder[T])The shared test package provides a generic Builder[T] that wraps polyfactory
to create type-safe builders for any Pydantic model. The with_* methods are
generated dynamically via __getattr__.
from polyfactory.factories.pydantic_factory import ModelFactory
from shared.factories import Builder
class GroupFactory(ModelFactory[Group]):
__model__ = Group
class GroupBuilder(Builder[Group]):
_factory = GroupFactory# Arrange - default random data
expected = GroupBuilder().build()
# Arrange - override specific fields
expected = GroupBuilder().with_name("Custom Name").with_type(GroupType.RECURRING).build()
# Arrange - build a batch
groups = GroupBuilder().with_type(GroupType.RECURRING).build_batch(5)def Should_CreateGroup():
sut = GroupService(Mock())
sut.create_group(CreateGroupRequest(name="Test"))def Should_SaveGroup_When_Valid(sut: GroupService):
sut.create_group(CreateGroupRequest(name="Test"))
assert Truedef should_create_group_successfully():
...# Act & Assert ← FORBIDDEN, always separate
with pytest.raises(ValueError):
sut.do_something()try:
sut.inject(secrets)
assert os.environ["KEY"] == "value"
finally:
os.environ.pop("KEY", None)# Assert
if result is not None: # ← FORBIDDEN, split into separate tests
assert result.name == "Test"Tests MUST mirror the production code structure using descriptive file naming.
Production code structure:
src/apps/myapp/
├── lambda_handler.py
├── infrastructure/
│ ├── config.py
│ ├── container.py
│ └── logging/
│ ├── json_formatter.py
│ └── logger_factory.py
├── application/
│ └── handlers/
│ └── create_user.py
└── domain/
└── entities/
└── user.pyTest structure (hierarchical mirror):
test/apps/myapp/
├── test_lambda_handler.py # mirrors lambda_handler.py
├── infrastructure/
│ ├── test_config.py # mirrors infrastructure/config.py
│ ├── test_container.py # mirrors infrastructure/container.py
│ └── logging/
│ ├── test_json_formatter.py # mirrors infrastructure/logging/json_formatter.py
│ └── test_logger_factory.py # mirrors infrastructure/logging/logger_factory.py
├── application/
│ └── handlers/
│ └── test_create_user.py # mirrors application/handlers/create_user.py
└── domain/
└── entities/
└── test_user.py # mirrors domain/entities/user.pyNaming convention:
{path}/test_{module}.pytest_REQUIRED configuration in pyproject.toml:
[tool.pytest.ini_options]
pythonpath = ["../../../src/apps/myapp"] # Adjust path to your src directory
testpaths = ["."]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["Should_*"]
asyncio_mode = "auto"
markers = [
"acceptance: marks tests as acceptance tests (require Docker)",
"unit: marks tests as unit tests (fast, no dependencies)",
"integration: marks tests as integration tests",
]⚠️ Important: The pythonpath must point to your production code directory to ensure imports work correctly
from test files.
Tests can be grouped in classes prefixed with Test*:
class TestProcessInvoiceHandler:
async def Should_SaveRequest_When_ValidInput(self) -> None:
# Arrange / Act / Assert
...Use markers to categorize tests and run them selectively:
@pytest.mark.acceptance
class TestLambdaAcceptance:
def Should_SaveToS3_When_LambdaInvoked(self) -> None:
...
@pytest.mark.unit
def Should_ValidateInput_When_EmptyName() -> None:
...Run specific markers: pytest -m "not acceptance" or pytest -m unit
This skill enforces:
Should_{ExpectedBehavior}_When_{Condition}sut / actual / expected variablesIf a test doesn't follow this → it fails review.
© macalbert, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files in .github/skills/python-testing of macalbert/envilder.
Open the folder on GitHubat commit b6a0327
Python Testing next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Python Testing this skillmacalbert/envilder | 138 | — | ~3.1k | Automated safety check: Pass | MIT | |
| JS-in-HTML Testingliaohch3/claude-tap | 3.3k | — | ~924 | Automated safety check: Pass | MIT | |
| E2E TestingOpentrons/opentrons | 521 | — | ~3k | Automated safety check: Notes | Apache-2.0 | |
| Claude Code QAPramodDutta/qaskills | 232 | — | ~2.3k | Automated safety check: Pass | MIT | |
| E2E Agent Browserjh941213/my-cc-harness | 126 | — | ~3.1k | Automated safety check: Notes | None | |
| Testing Patternssoftspark/ai-toolkit | 179 | — | ~1.6k | Automated safety check: Pass | Apache-2.0 |
liaohch3/claude-tap
Tests JavaScript embedded in an HTML file in two layers: pytest checks of the logic ported to Python, and Playwright runs in a real browser for the DOM.
Opentrons/opentrons
E2E testing conventions for Protocol Designer and Labware Library using Playwright + pytest in e2e-testing/.
PramodDutta/qaskills
The complete QA skill for Claude Code — turn Claude into an expert QA engineer that picks the right test type, writes reliable Playwright, Cypress, and pytest tests, eliminates flaky tests, enforces…
jh941213/my-cc-harness
E2E test automation using agent-browser CLI. An agent skill from jh941213/my-cc-harness.
softspark/ai-toolkit
Testing strategy: pyramid, AAA, mocks/fakes/stubs, flaky tests, coverage.
ArabelaTso/Skills-4-SE
Explains test failures and provides actionable debugging guidance.
macalbert/envilder
Five independent analysis perspectives for code review: correctness, architecture, security, conventions, and complexity.
macalbert/envilder
Index of Architecture Decision Records (ADRs) for cross-cutting technical decisions.
macalbert/envilder
Git commit messages, PR workflow, and branching strategy using Conventional Commits and Semantic Versioning.
macalbert/envilder
Mandatory testing conventions including the narrow diagnostic exception for testing test-only code, AAA pattern, test naming, and assertions across all stacks (.NET, TypeScript, Python).
macalbert/envilder
Workflow for maintaining changelogs, READMEs, and documentation files.
macalbert/envilder
Audit and synchronize documentation across website, READMEs, and docs/.
Works with
Categories
Mandatory testing conventions including AAA pattern, test naming, assertions, and mocks. Python Testing is an agent skill from macalbert/envilder. Mandatory testing conventions including AAA pattern, test naming, assertions, and mocks.
Python Testing fits situations like: E2E tests with pytest; tasks that involve Unit testing; tasks that involve End-to-end testing.
Run `npx skills add macalbert/envilder --skill python-testing -a claude-code`. Or copy the skill folder (.github/skills/python-testing in macalbert/envilder) into .claude/skills/python-testing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add macalbert/envilder --skill python-testing -a codex`. Or copy the skill folder (.github/skills/python-testing in macalbert/envilder) into .agents/skills/python-testing in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add macalbert/envilder --skill python-testing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/python-testing, .gemini/skills/python-testing, .github/skills/python-testing and .opencode/skills/python-testing in your project.
Going by SKILL.md and its folder, Python Testing needs the command-line tools its instructions call (pytest). Our summary lists: Python 3; Docker.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Python Testing is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.1k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Python Testing: JS-in-HTML Testing (liaohch3/claude-tap, 3.3k stars), E2E Testing (Opentrons/opentrons, 521 stars), Claude Code QA (PramodDutta/qaskills, 232 stars) and E2E Agent Browser (jh941213/my-cc-harness, 126 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
macalbert (a GitHub user) maintains it in macalbert/envilder, which has 138 GitHub stars. The repository holds 30 skills in this directory. The repository was last updated on October 5, 2026.
Source: macalbert/envilder on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.