@jhl8041/dooray-mcp 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/bmad_agent_bmad-master.md +14 -0
- package/.claude/commands/bmad_bmm_agent_analyst.md +14 -0
- package/.claude/commands/bmad_bmm_agent_architect.md +14 -0
- package/.claude/commands/bmad_bmm_agent_dev.md +14 -0
- package/.claude/commands/bmad_bmm_agent_pm.md +14 -0
- package/.claude/commands/bmad_bmm_agent_quick-flow-solo-dev.md +14 -0
- package/.claude/commands/bmad_bmm_agent_sm.md +14 -0
- package/.claude/commands/bmad_bmm_agent_tea.md +14 -0
- package/.claude/commands/bmad_bmm_agent_tech-writer_tech-writer.md +14 -0
- package/.claude/commands/bmad_bmm_agent_ux-designer.md +14 -0
- package/.claude/commands/bmad_bmm_check-implementation-readiness.md +5 -0
- package/.claude/commands/bmad_bmm_code-review.md +13 -0
- package/.claude/commands/bmad_bmm_correct-course.md +13 -0
- package/.claude/commands/bmad_bmm_create-architecture.md +5 -0
- package/.claude/commands/bmad_bmm_create-epics-and-stories.md +5 -0
- package/.claude/commands/bmad_bmm_create-excalidraw-dataflow.md +13 -0
- package/.claude/commands/bmad_bmm_create-excalidraw-diagram.md +13 -0
- package/.claude/commands/bmad_bmm_create-excalidraw-flowchart.md +13 -0
- package/.claude/commands/bmad_bmm_create-excalidraw-wireframe.md +13 -0
- package/.claude/commands/bmad_bmm_create-prd.md +5 -0
- package/.claude/commands/bmad_bmm_create-product-brief.md +5 -0
- package/.claude/commands/bmad_bmm_create-story.md +13 -0
- package/.claude/commands/bmad_bmm_create-ux-design.md +5 -0
- package/.claude/commands/bmad_bmm_dev-story.md +13 -0
- package/.claude/commands/bmad_bmm_document-project.md +13 -0
- package/.claude/commands/bmad_bmm_quick-dev.md +5 -0
- package/.claude/commands/bmad_bmm_quick-spec.md +5 -0
- package/.claude/commands/bmad_bmm_research.md +5 -0
- package/.claude/commands/bmad_bmm_retrospective.md +13 -0
- package/.claude/commands/bmad_bmm_sprint-planning.md +13 -0
- package/.claude/commands/bmad_bmm_sprint-status.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-atdd.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-automate.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-ci.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-framework.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-nfr.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-test-design.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-test-review.md +13 -0
- package/.claude/commands/bmad_bmm_testarch-trace.md +13 -0
- package/.claude/commands/bmad_brainstorming.md +5 -0
- package/.claude/commands/bmad_editorial-review-prose.md +9 -0
- package/.claude/commands/bmad_editorial-review-structure.md +10 -0
- package/.claude/commands/bmad_help.md +9 -0
- package/.claude/commands/bmad_index-docs.md +9 -0
- package/.claude/commands/bmad_party-mode.md +5 -0
- package/.claude/commands/bmad_review-adversarial-general.md +9 -0
- package/.claude/commands/bmad_shard-doc.md +9 -0
- package/_bmad/_config/agent-manifest.csv +11 -0
- package/_bmad/_config/agents/bmm-analyst.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-architect.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-dev.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-pm.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-quick-flow-solo-dev.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-sm.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-tea.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-tech-writer.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-ux-designer.customize.yaml +41 -0
- package/_bmad/_config/agents/core-bmad-master.customize.yaml +41 -0
- package/_bmad/_config/bmad-help.csv +40 -0
- package/_bmad/_config/files-manifest.csv +281 -0
- package/_bmad/_config/manifest.yaml +21 -0
- package/_bmad/_config/task-manifest.csv +9 -0
- package/_bmad/_config/tool-manifest.csv +1 -0
- package/_bmad/_config/workflow-manifest.csv +32 -0
- package/_bmad/bmm/agents/analyst.md +76 -0
- package/_bmad/bmm/agents/architect.md +58 -0
- package/_bmad/bmm/agents/dev.md +69 -0
- package/_bmad/bmm/agents/pm.md +72 -0
- package/_bmad/bmm/agents/quick-flow-solo-dev.md +69 -0
- package/_bmad/bmm/agents/sm.md +70 -0
- package/_bmad/bmm/agents/tea.md +70 -0
- package/_bmad/bmm/agents/tech-writer/tech-writer.md +70 -0
- package/_bmad/bmm/agents/ux-designer.md +57 -0
- package/_bmad/bmm/config.yaml +16 -0
- package/_bmad/bmm/data/project-context-template.md +26 -0
- package/_bmad/bmm/module-help.csv +32 -0
- package/_bmad/bmm/teams/default-party.csv +21 -0
- package/_bmad/bmm/teams/team-fullstack.yaml +12 -0
- package/_bmad/bmm/testarch/knowledge/adr-quality-readiness-checklist.md +350 -0
- package/_bmad/bmm/testarch/knowledge/api-request.md +442 -0
- package/_bmad/bmm/testarch/knowledge/api-testing-patterns.md +843 -0
- package/_bmad/bmm/testarch/knowledge/auth-session.md +552 -0
- package/_bmad/bmm/testarch/knowledge/burn-in.md +273 -0
- package/_bmad/bmm/testarch/knowledge/ci-burn-in.md +675 -0
- package/_bmad/bmm/testarch/knowledge/component-tdd.md +486 -0
- package/_bmad/bmm/testarch/knowledge/contract-testing.md +957 -0
- package/_bmad/bmm/testarch/knowledge/data-factories.md +500 -0
- package/_bmad/bmm/testarch/knowledge/email-auth.md +721 -0
- package/_bmad/bmm/testarch/knowledge/error-handling.md +725 -0
- package/_bmad/bmm/testarch/knowledge/feature-flags.md +750 -0
- package/_bmad/bmm/testarch/knowledge/file-utils.md +463 -0
- package/_bmad/bmm/testarch/knowledge/fixture-architecture.md +401 -0
- package/_bmad/bmm/testarch/knowledge/fixtures-composition.md +382 -0
- package/_bmad/bmm/testarch/knowledge/intercept-network-call.md +430 -0
- package/_bmad/bmm/testarch/knowledge/log.md +429 -0
- package/_bmad/bmm/testarch/knowledge/network-error-monitor.md +405 -0
- package/_bmad/bmm/testarch/knowledge/network-first.md +486 -0
- package/_bmad/bmm/testarch/knowledge/network-recorder.md +527 -0
- package/_bmad/bmm/testarch/knowledge/nfr-criteria.md +670 -0
- package/_bmad/bmm/testarch/knowledge/overview.md +286 -0
- package/_bmad/bmm/testarch/knowledge/playwright-config.md +730 -0
- package/_bmad/bmm/testarch/knowledge/probability-impact.md +601 -0
- package/_bmad/bmm/testarch/knowledge/recurse.md +421 -0
- package/_bmad/bmm/testarch/knowledge/risk-governance.md +615 -0
- package/_bmad/bmm/testarch/knowledge/selective-testing.md +732 -0
- package/_bmad/bmm/testarch/knowledge/selector-resilience.md +527 -0
- package/_bmad/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
- package/_bmad/bmm/testarch/knowledge/test-levels-framework.md +473 -0
- package/_bmad/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
- package/_bmad/bmm/testarch/knowledge/test-quality.md +664 -0
- package/_bmad/bmm/testarch/knowledge/timing-debugging.md +372 -0
- package/_bmad/bmm/testarch/knowledge/visual-debugging.md +524 -0
- package/_bmad/bmm/testarch/tea-index.csv +35 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
- package/_bmad/bmm/workflows/1-analysis/research/research.template.md +29 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
- package/_bmad/bmm/workflows/1-analysis/research/workflow.md +173 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +13 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +197 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +11 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01-init.md +191 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +153 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +224 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +226 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +213 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +207 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +226 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +237 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +228 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +231 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +242 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +217 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +124 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +247 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +249 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +253 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +168 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +218 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +191 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +209 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +174 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +228 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +217 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +263 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +209 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +242 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +231 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/templates/prd-template.md +10 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +433 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow.md +150 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +135 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +331 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md +50 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/workflow.yaml +50 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml +58 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/instructions.xml +345 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/template.md +49 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml +59 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml +25 -0
- package/_bmad/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
- package/_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml +57 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml +35 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +176 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +149 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +192 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +145 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +128 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
- package/_bmad/bmm/workflows/document-project/checklist.md +245 -0
- package/_bmad/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/_bmad/bmm/workflows/document-project/instructions.md +221 -0
- package/_bmad/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/_bmad/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/_bmad/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/_bmad/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/_bmad/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/_bmad/bmm/workflows/document-project/workflow.yaml +28 -0
- package/_bmad/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/_bmad/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/_bmad/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/_bmad/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/testarch/atdd/atdd-checklist-template.md +363 -0
- package/_bmad/bmm/workflows/testarch/atdd/checklist.md +374 -0
- package/_bmad/bmm/workflows/testarch/atdd/instructions.md +806 -0
- package/_bmad/bmm/workflows/testarch/atdd/workflow.yaml +45 -0
- package/_bmad/bmm/workflows/testarch/automate/checklist.md +582 -0
- package/_bmad/bmm/workflows/testarch/automate/instructions.md +1324 -0
- package/_bmad/bmm/workflows/testarch/automate/workflow.yaml +52 -0
- package/_bmad/bmm/workflows/testarch/ci/checklist.md +247 -0
- package/_bmad/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
- package/_bmad/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
- package/_bmad/bmm/workflows/testarch/ci/instructions.md +536 -0
- package/_bmad/bmm/workflows/testarch/ci/workflow.yaml +45 -0
- package/_bmad/bmm/workflows/testarch/framework/checklist.md +320 -0
- package/_bmad/bmm/workflows/testarch/framework/instructions.md +481 -0
- package/_bmad/bmm/workflows/testarch/framework/workflow.yaml +47 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/instructions.md +726 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +461 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/workflow.yaml +47 -0
- package/_bmad/bmm/workflows/testarch/test-design/checklist.md +407 -0
- package/_bmad/bmm/workflows/testarch/test-design/instructions.md +1158 -0
- package/_bmad/bmm/workflows/testarch/test-design/test-design-architecture-template.md +213 -0
- package/_bmad/bmm/workflows/testarch/test-design/test-design-qa-template.md +286 -0
- package/_bmad/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
- package/_bmad/bmm/workflows/testarch/test-design/workflow.yaml +69 -0
- package/_bmad/bmm/workflows/testarch/test-review/checklist.md +472 -0
- package/_bmad/bmm/workflows/testarch/test-review/instructions.md +628 -0
- package/_bmad/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
- package/_bmad/bmm/workflows/testarch/test-review/workflow.yaml +46 -0
- package/_bmad/bmm/workflows/testarch/trace/checklist.md +642 -0
- package/_bmad/bmm/workflows/testarch/trace/instructions.md +1030 -0
- package/_bmad/bmm/workflows/testarch/trace/trace-template.md +675 -0
- package/_bmad/bmm/workflows/testarch/trace/workflow.yaml +55 -0
- package/_bmad/core/agents/bmad-master.md +56 -0
- package/_bmad/core/config.yaml +9 -0
- package/_bmad/core/module-help.csv +9 -0
- package/_bmad/core/resources/excalidraw/README.md +160 -0
- package/_bmad/core/resources/excalidraw/excalidraw-helpers.md +127 -0
- package/_bmad/core/resources/excalidraw/library-loader.md +50 -0
- package/_bmad/core/resources/excalidraw/validate-json-instructions.md +79 -0
- package/_bmad/core/tasks/editorial-review-prose.xml +100 -0
- package/_bmad/core/tasks/editorial-review-structure.xml +209 -0
- package/_bmad/core/tasks/help.md +62 -0
- package/_bmad/core/tasks/index-docs.xml +65 -0
- package/_bmad/core/tasks/review-adversarial-general.xml +48 -0
- package/_bmad/core/tasks/shard-doc.xml +109 -0
- package/_bmad/core/tasks/workflow.xml +235 -0
- package/_bmad/core/workflows/advanced-elicitation/methods.csv +51 -0
- package/_bmad/core/workflows/advanced-elicitation/workflow.xml +117 -0
- package/_bmad/core/workflows/brainstorming/brain-methods.csv +62 -0
- package/_bmad/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
- package/_bmad/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
- package/_bmad/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -0
- package/_bmad/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
- package/_bmad/core/workflows/brainstorming/template.md +15 -0
- package/_bmad/core/workflows/brainstorming/workflow.md +58 -0
- package/_bmad/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -0
- package/_bmad/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
- package/_bmad/core/workflows/party-mode/steps/step-03-graceful-exit.md +157 -0
- package/_bmad/core/workflows/party-mode/workflow.md +194 -0
- package/dist/api/client.d.ts +16 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +91 -5
- package/dist/api/client.js.map +1 -1
- package/dist/api/projects.d.ts +23 -2
- package/dist/api/projects.d.ts.map +1 -1
- package/dist/api/projects.js +32 -0
- package/dist/api/projects.js.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/projects/delete-attachment-from-task.d.ts.map +1 -0
- package/dist/tools/projects/delete-attachment-from-task.js.map +1 -0
- package/dist/tools/projects/delete-attachment.d.ts +55 -0
- package/dist/tools/projects/delete-attachment.d.ts.map +1 -0
- package/dist/tools/projects/delete-attachment.js +115 -0
- package/dist/tools/projects/delete-attachment.js.map +1 -0
- package/dist/tools/projects/download-attachment.d.ts +55 -0
- package/dist/tools/projects/download-attachment.d.ts.map +1 -0
- package/dist/tools/projects/download-attachment.js +153 -0
- package/dist/tools/projects/download-attachment.js.map +1 -0
- package/dist/tools/projects/get-attachment-list.d.ts +48 -0
- package/dist/tools/projects/get-attachment-list.d.ts.map +1 -0
- package/dist/tools/projects/get-attachment-list.js +118 -0
- package/dist/tools/projects/get-attachment-list.js.map +1 -0
- package/dist/tools/projects/get-attachment-metadata.d.ts +55 -0
- package/dist/tools/projects/get-attachment-metadata.d.ts.map +1 -0
- package/dist/tools/projects/get-attachment-metadata.js +120 -0
- package/dist/tools/projects/get-attachment-metadata.js.map +1 -0
- package/dist/tools/projects/register-attachment-to-task.d.ts.map +1 -0
- package/dist/tools/projects/register-attachment-to-task.js.map +1 -0
- package/dist/tools/projects/update-task.d.ts +4 -4
- package/dist/tools/projects/update-task.js +12 -12
- package/dist/tools/projects/update-task.js.map +1 -1
- package/dist/tools/projects/upload-attachment.d.ts +69 -0
- package/dist/tools/projects/upload-attachment.d.ts.map +1 -0
- package/dist/tools/projects/upload-attachment.js +109 -0
- package/dist/tools/projects/upload-attachment.js.map +1 -0
- package/dist/types/dooray-api.d.ts +15 -0
- package/dist/types/dooray-api.d.ts.map +1 -1
- package/docs/architecture.md +227 -0
- package/docs/development-guide.md +336 -0
- package/docs/index.md +115 -0
- package/docs/project-overview.md +105 -0
- package/docs/project-scan-report.json +120 -0
- package/docs/source-tree-analysis.md +240 -0
- package/package.json +1 -1
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
# API Testing Patterns
|
|
2
|
+
|
|
3
|
+
## Principle
|
|
4
|
+
|
|
5
|
+
Test APIs and backend services directly without browser overhead. Use Playwright's `request` context for HTTP operations, `apiRequest` utility for enhanced features, and `recurse` for async operations. Pure API tests run faster, are more stable, and provide better coverage for service-layer logic.
|
|
6
|
+
|
|
7
|
+
## Rationale
|
|
8
|
+
|
|
9
|
+
Many teams over-rely on E2E/browser tests when API tests would be more appropriate:
|
|
10
|
+
|
|
11
|
+
- **Slower feedback**: Browser tests take seconds, API tests take milliseconds
|
|
12
|
+
- **More brittle**: UI changes break tests even when API works correctly
|
|
13
|
+
- **Wrong abstraction**: Testing business logic through UI layers adds noise
|
|
14
|
+
- **Resource heavy**: Browsers consume memory and CPU
|
|
15
|
+
|
|
16
|
+
API-first testing provides:
|
|
17
|
+
|
|
18
|
+
- **Fast execution**: No browser startup, no rendering, no JavaScript execution
|
|
19
|
+
- **Direct validation**: Test exactly what the service returns
|
|
20
|
+
- **Better isolation**: Test service logic independent of UI
|
|
21
|
+
- **Easier debugging**: Clear request/response without DOM noise
|
|
22
|
+
- **Contract validation**: Verify API contracts explicitly
|
|
23
|
+
|
|
24
|
+
## When to Use API Tests vs E2E Tests
|
|
25
|
+
|
|
26
|
+
| Scenario | API Test | E2E Test |
|
|
27
|
+
|----------|----------|----------|
|
|
28
|
+
| CRUD operations | ✅ Primary | ❌ Overkill |
|
|
29
|
+
| Business logic validation | ✅ Primary | ❌ Overkill |
|
|
30
|
+
| Error handling (4xx, 5xx) | ✅ Primary | ⚠️ Supplement |
|
|
31
|
+
| Authentication flows | ✅ Primary | ⚠️ Supplement |
|
|
32
|
+
| Data transformation | ✅ Primary | ❌ Overkill |
|
|
33
|
+
| User journeys | ❌ Can't test | ✅ Primary |
|
|
34
|
+
| Visual regression | ❌ Can't test | ✅ Primary |
|
|
35
|
+
| Cross-browser issues | ❌ Can't test | ✅ Primary |
|
|
36
|
+
|
|
37
|
+
**Rule of thumb**: If you're testing what the server returns (not how it looks), use API tests.
|
|
38
|
+
|
|
39
|
+
## Pattern Examples
|
|
40
|
+
|
|
41
|
+
### Example 1: Pure API Test (No Browser)
|
|
42
|
+
|
|
43
|
+
**Context**: Test REST API endpoints directly without any browser context.
|
|
44
|
+
|
|
45
|
+
**Implementation**:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// tests/api/users.spec.ts
|
|
49
|
+
import { test, expect } from '@playwright/test';
|
|
50
|
+
|
|
51
|
+
// No page, no browser - just API
|
|
52
|
+
test.describe('Users API', () => {
|
|
53
|
+
test('should create user', async ({ request }) => {
|
|
54
|
+
const response = await request.post('/api/users', {
|
|
55
|
+
data: {
|
|
56
|
+
name: 'John Doe',
|
|
57
|
+
email: 'john@example.com',
|
|
58
|
+
role: 'user',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(response.status()).toBe(201);
|
|
63
|
+
|
|
64
|
+
const user = await response.json();
|
|
65
|
+
expect(user.id).toBeDefined();
|
|
66
|
+
expect(user.name).toBe('John Doe');
|
|
67
|
+
expect(user.email).toBe('john@example.com');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('should get user by ID', async ({ request }) => {
|
|
71
|
+
// Create user first
|
|
72
|
+
const createResponse = await request.post('/api/users', {
|
|
73
|
+
data: { name: 'Jane Doe', email: 'jane@example.com' },
|
|
74
|
+
});
|
|
75
|
+
const { id } = await createResponse.json();
|
|
76
|
+
|
|
77
|
+
// Get user
|
|
78
|
+
const getResponse = await request.get(`/api/users/${id}`);
|
|
79
|
+
expect(getResponse.status()).toBe(200);
|
|
80
|
+
|
|
81
|
+
const user = await getResponse.json();
|
|
82
|
+
expect(user.id).toBe(id);
|
|
83
|
+
expect(user.name).toBe('Jane Doe');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('should return 404 for non-existent user', async ({ request }) => {
|
|
87
|
+
const response = await request.get('/api/users/non-existent-id');
|
|
88
|
+
expect(response.status()).toBe(404);
|
|
89
|
+
|
|
90
|
+
const error = await response.json();
|
|
91
|
+
expect(error.code).toBe('USER_NOT_FOUND');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should validate required fields', async ({ request }) => {
|
|
95
|
+
const response = await request.post('/api/users', {
|
|
96
|
+
data: { name: 'Missing Email' }, // email is required
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(response.status()).toBe(400);
|
|
100
|
+
|
|
101
|
+
const error = await response.json();
|
|
102
|
+
expect(error.code).toBe('VALIDATION_ERROR');
|
|
103
|
+
expect(error.details).toContainEqual(
|
|
104
|
+
expect.objectContaining({ field: 'email', message: expect.any(String) })
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Key Points**:
|
|
111
|
+
|
|
112
|
+
- No `page` fixture needed - only `request`
|
|
113
|
+
- Tests run without browser overhead
|
|
114
|
+
- Direct HTTP assertions
|
|
115
|
+
- Clear error handling tests
|
|
116
|
+
|
|
117
|
+
### Example 2: API Test with apiRequest Utility
|
|
118
|
+
|
|
119
|
+
**Context**: Use enhanced apiRequest for schema validation, retry, and type safety.
|
|
120
|
+
|
|
121
|
+
**Implementation**:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// tests/api/orders.spec.ts
|
|
125
|
+
import { test, expect } from '@seontechnologies/playwright-utils/api-request/fixtures';
|
|
126
|
+
import { z } from 'zod';
|
|
127
|
+
|
|
128
|
+
// Define schema for type safety and validation
|
|
129
|
+
const OrderSchema = z.object({
|
|
130
|
+
id: z.string().uuid(),
|
|
131
|
+
userId: z.string(),
|
|
132
|
+
items: z.array(
|
|
133
|
+
z.object({
|
|
134
|
+
productId: z.string(),
|
|
135
|
+
quantity: z.number().positive(),
|
|
136
|
+
price: z.number().positive(),
|
|
137
|
+
})
|
|
138
|
+
),
|
|
139
|
+
total: z.number().positive(),
|
|
140
|
+
status: z.enum(['pending', 'processing', 'shipped', 'delivered']),
|
|
141
|
+
createdAt: z.string().datetime(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
type Order = z.infer<typeof OrderSchema>;
|
|
145
|
+
|
|
146
|
+
test.describe('Orders API', () => {
|
|
147
|
+
test('should create order with schema validation', async ({ apiRequest }) => {
|
|
148
|
+
const { status, body } = await apiRequest<Order>({
|
|
149
|
+
method: 'POST',
|
|
150
|
+
path: '/api/orders',
|
|
151
|
+
body: {
|
|
152
|
+
userId: 'user-123',
|
|
153
|
+
items: [
|
|
154
|
+
{ productId: 'prod-1', quantity: 2, price: 29.99 },
|
|
155
|
+
{ productId: 'prod-2', quantity: 1, price: 49.99 },
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
validateSchema: OrderSchema, // Validates response matches schema
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(status).toBe(201);
|
|
162
|
+
expect(body.id).toBeDefined();
|
|
163
|
+
expect(body.status).toBe('pending');
|
|
164
|
+
expect(body.total).toBe(109.97); // 2*29.99 + 49.99
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('should handle server errors with retry', async ({ apiRequest }) => {
|
|
168
|
+
// apiRequest retries 5xx errors by default
|
|
169
|
+
const { status, body } = await apiRequest({
|
|
170
|
+
method: 'GET',
|
|
171
|
+
path: '/api/orders/order-123',
|
|
172
|
+
retryConfig: {
|
|
173
|
+
maxRetries: 3,
|
|
174
|
+
retryDelay: 1000,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(status).toBe(200);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('should list orders with pagination', async ({ apiRequest }) => {
|
|
182
|
+
const { status, body } = await apiRequest<{ orders: Order[]; total: number; page: number }>({
|
|
183
|
+
method: 'GET',
|
|
184
|
+
path: '/api/orders',
|
|
185
|
+
params: { page: 1, limit: 10, status: 'pending' },
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(status).toBe(200);
|
|
189
|
+
expect(body.orders).toHaveLength(10);
|
|
190
|
+
expect(body.total).toBeGreaterThan(10);
|
|
191
|
+
expect(body.page).toBe(1);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Key Points**:
|
|
197
|
+
|
|
198
|
+
- Zod schema for runtime validation AND TypeScript types
|
|
199
|
+
- `validateSchema` throws if response doesn't match
|
|
200
|
+
- Built-in retry for transient failures
|
|
201
|
+
- Type-safe `body` access
|
|
202
|
+
|
|
203
|
+
### Example 3: Microservice-to-Microservice Testing
|
|
204
|
+
|
|
205
|
+
**Context**: Test service interactions without browser - validate API contracts between services.
|
|
206
|
+
|
|
207
|
+
**Implementation**:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// tests/api/service-integration.spec.ts
|
|
211
|
+
import { test, expect } from '@seontechnologies/playwright-utils/fixtures';
|
|
212
|
+
|
|
213
|
+
test.describe('Service Integration', () => {
|
|
214
|
+
const USER_SERVICE_URL = process.env.USER_SERVICE_URL || 'http://localhost:3001';
|
|
215
|
+
const ORDER_SERVICE_URL = process.env.ORDER_SERVICE_URL || 'http://localhost:3002';
|
|
216
|
+
const INVENTORY_SERVICE_URL = process.env.INVENTORY_SERVICE_URL || 'http://localhost:3003';
|
|
217
|
+
|
|
218
|
+
test('order service should validate user exists', async ({ apiRequest }) => {
|
|
219
|
+
// Create user in user-service
|
|
220
|
+
const { body: user } = await apiRequest({
|
|
221
|
+
method: 'POST',
|
|
222
|
+
path: '/api/users',
|
|
223
|
+
baseUrl: USER_SERVICE_URL,
|
|
224
|
+
body: { name: 'Test User', email: 'test@example.com' },
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Create order in order-service (should validate user via user-service)
|
|
228
|
+
const { status, body: order } = await apiRequest({
|
|
229
|
+
method: 'POST',
|
|
230
|
+
path: '/api/orders',
|
|
231
|
+
baseUrl: ORDER_SERVICE_URL,
|
|
232
|
+
body: {
|
|
233
|
+
userId: user.id,
|
|
234
|
+
items: [{ productId: 'prod-1', quantity: 1 }],
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(status).toBe(201);
|
|
239
|
+
expect(order.userId).toBe(user.id);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('order service should reject invalid user', async ({ apiRequest }) => {
|
|
243
|
+
const { status, body } = await apiRequest({
|
|
244
|
+
method: 'POST',
|
|
245
|
+
path: '/api/orders',
|
|
246
|
+
baseUrl: ORDER_SERVICE_URL,
|
|
247
|
+
body: {
|
|
248
|
+
userId: 'non-existent-user',
|
|
249
|
+
items: [{ productId: 'prod-1', quantity: 1 }],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
expect(status).toBe(400);
|
|
254
|
+
expect(body.code).toBe('INVALID_USER');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('order should decrease inventory', async ({ apiRequest, recurse }) => {
|
|
258
|
+
// Get initial inventory
|
|
259
|
+
const { body: initialInventory } = await apiRequest({
|
|
260
|
+
method: 'GET',
|
|
261
|
+
path: '/api/inventory/prod-1',
|
|
262
|
+
baseUrl: INVENTORY_SERVICE_URL,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Create order
|
|
266
|
+
await apiRequest({
|
|
267
|
+
method: 'POST',
|
|
268
|
+
path: '/api/orders',
|
|
269
|
+
baseUrl: ORDER_SERVICE_URL,
|
|
270
|
+
body: {
|
|
271
|
+
userId: 'user-123',
|
|
272
|
+
items: [{ productId: 'prod-1', quantity: 2 }],
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Poll for inventory update (eventual consistency)
|
|
277
|
+
const { body: updatedInventory } = await recurse(
|
|
278
|
+
() =>
|
|
279
|
+
apiRequest({
|
|
280
|
+
method: 'GET',
|
|
281
|
+
path: '/api/inventory/prod-1',
|
|
282
|
+
baseUrl: INVENTORY_SERVICE_URL,
|
|
283
|
+
}),
|
|
284
|
+
(response) => response.body.quantity === initialInventory.quantity - 2,
|
|
285
|
+
{ timeout: 10000, interval: 500 }
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
expect(updatedInventory.quantity).toBe(initialInventory.quantity - 2);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Key Points**:
|
|
294
|
+
|
|
295
|
+
- Multiple service URLs for microservice testing
|
|
296
|
+
- Tests service-to-service communication
|
|
297
|
+
- Uses `recurse` for eventual consistency
|
|
298
|
+
- No browser needed for full integration testing
|
|
299
|
+
|
|
300
|
+
### Example 4: GraphQL API Testing
|
|
301
|
+
|
|
302
|
+
**Context**: Test GraphQL endpoints with queries and mutations.
|
|
303
|
+
|
|
304
|
+
**Implementation**:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// tests/api/graphql.spec.ts
|
|
308
|
+
import { test, expect } from '@seontechnologies/playwright-utils/api-request/fixtures';
|
|
309
|
+
|
|
310
|
+
const GRAPHQL_ENDPOINT = '/graphql';
|
|
311
|
+
|
|
312
|
+
test.describe('GraphQL API', () => {
|
|
313
|
+
test('should query users', async ({ apiRequest }) => {
|
|
314
|
+
const query = `
|
|
315
|
+
query GetUsers($limit: Int) {
|
|
316
|
+
users(limit: $limit) {
|
|
317
|
+
id
|
|
318
|
+
name
|
|
319
|
+
email
|
|
320
|
+
role
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
const { status, body } = await apiRequest({
|
|
326
|
+
method: 'POST',
|
|
327
|
+
path: GRAPHQL_ENDPOINT,
|
|
328
|
+
body: {
|
|
329
|
+
query,
|
|
330
|
+
variables: { limit: 10 },
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expect(status).toBe(200);
|
|
335
|
+
expect(body.errors).toBeUndefined();
|
|
336
|
+
expect(body.data.users).toHaveLength(10);
|
|
337
|
+
expect(body.data.users[0]).toHaveProperty('id');
|
|
338
|
+
expect(body.data.users[0]).toHaveProperty('name');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test('should create user via mutation', async ({ apiRequest }) => {
|
|
342
|
+
const mutation = `
|
|
343
|
+
mutation CreateUser($input: CreateUserInput!) {
|
|
344
|
+
createUser(input: $input) {
|
|
345
|
+
id
|
|
346
|
+
name
|
|
347
|
+
email
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
`;
|
|
351
|
+
|
|
352
|
+
const { status, body } = await apiRequest({
|
|
353
|
+
method: 'POST',
|
|
354
|
+
path: GRAPHQL_ENDPOINT,
|
|
355
|
+
body: {
|
|
356
|
+
query: mutation,
|
|
357
|
+
variables: {
|
|
358
|
+
input: {
|
|
359
|
+
name: 'GraphQL User',
|
|
360
|
+
email: 'graphql@example.com',
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(status).toBe(200);
|
|
367
|
+
expect(body.errors).toBeUndefined();
|
|
368
|
+
expect(body.data.createUser.id).toBeDefined();
|
|
369
|
+
expect(body.data.createUser.name).toBe('GraphQL User');
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test('should handle GraphQL errors', async ({ apiRequest }) => {
|
|
373
|
+
const query = `
|
|
374
|
+
query GetUser($id: ID!) {
|
|
375
|
+
user(id: $id) {
|
|
376
|
+
id
|
|
377
|
+
name
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
`;
|
|
381
|
+
|
|
382
|
+
const { status, body } = await apiRequest({
|
|
383
|
+
method: 'POST',
|
|
384
|
+
path: GRAPHQL_ENDPOINT,
|
|
385
|
+
body: {
|
|
386
|
+
query,
|
|
387
|
+
variables: { id: 'non-existent' },
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
expect(status).toBe(200); // GraphQL returns 200 even for errors
|
|
392
|
+
expect(body.errors).toBeDefined();
|
|
393
|
+
expect(body.errors[0].message).toContain('not found');
|
|
394
|
+
expect(body.data.user).toBeNull();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('should handle validation errors', async ({ apiRequest }) => {
|
|
398
|
+
const mutation = `
|
|
399
|
+
mutation CreateUser($input: CreateUserInput!) {
|
|
400
|
+
createUser(input: $input) {
|
|
401
|
+
id
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
`;
|
|
405
|
+
|
|
406
|
+
const { status, body } = await apiRequest({
|
|
407
|
+
method: 'POST',
|
|
408
|
+
path: GRAPHQL_ENDPOINT,
|
|
409
|
+
body: {
|
|
410
|
+
query: mutation,
|
|
411
|
+
variables: {
|
|
412
|
+
input: {
|
|
413
|
+
name: '', // Invalid: empty name
|
|
414
|
+
email: 'invalid-email', // Invalid: bad format
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
expect(status).toBe(200);
|
|
421
|
+
expect(body.errors).toBeDefined();
|
|
422
|
+
expect(body.errors[0].extensions.code).toBe('BAD_USER_INPUT');
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Key Points**:
|
|
428
|
+
|
|
429
|
+
- GraphQL queries and mutations via POST
|
|
430
|
+
- Variables passed in request body
|
|
431
|
+
- GraphQL returns 200 even for errors (check `body.errors`)
|
|
432
|
+
- Test validation and business logic errors
|
|
433
|
+
|
|
434
|
+
### Example 5: Database Seeding and Cleanup via API
|
|
435
|
+
|
|
436
|
+
**Context**: Use API calls to set up and tear down test data without direct database access.
|
|
437
|
+
|
|
438
|
+
**Implementation**:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// tests/api/with-data-setup.spec.ts
|
|
442
|
+
import { test, expect } from '@seontechnologies/playwright-utils/fixtures';
|
|
443
|
+
|
|
444
|
+
test.describe('Orders with Data Setup', () => {
|
|
445
|
+
let testUser: { id: string; email: string };
|
|
446
|
+
let testProducts: Array<{ id: string; name: string; price: number }>;
|
|
447
|
+
|
|
448
|
+
test.beforeAll(async ({ request }) => {
|
|
449
|
+
// Seed user via API
|
|
450
|
+
const userResponse = await request.post('/api/users', {
|
|
451
|
+
data: {
|
|
452
|
+
name: 'Test User',
|
|
453
|
+
email: `test-${Date.now()}@example.com`,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
testUser = await userResponse.json();
|
|
457
|
+
|
|
458
|
+
// Seed products via API
|
|
459
|
+
testProducts = [];
|
|
460
|
+
for (const product of [
|
|
461
|
+
{ name: 'Widget A', price: 29.99 },
|
|
462
|
+
{ name: 'Widget B', price: 49.99 },
|
|
463
|
+
{ name: 'Widget C', price: 99.99 },
|
|
464
|
+
]) {
|
|
465
|
+
const productResponse = await request.post('/api/products', {
|
|
466
|
+
data: product,
|
|
467
|
+
});
|
|
468
|
+
testProducts.push(await productResponse.json());
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test.afterAll(async ({ request }) => {
|
|
473
|
+
// Cleanup via API
|
|
474
|
+
if (testUser?.id) {
|
|
475
|
+
await request.delete(`/api/users/${testUser.id}`);
|
|
476
|
+
}
|
|
477
|
+
for (const product of testProducts) {
|
|
478
|
+
await request.delete(`/api/products/${product.id}`);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test('should create order with seeded data', async ({ apiRequest }) => {
|
|
483
|
+
const { status, body } = await apiRequest({
|
|
484
|
+
method: 'POST',
|
|
485
|
+
path: '/api/orders',
|
|
486
|
+
body: {
|
|
487
|
+
userId: testUser.id,
|
|
488
|
+
items: [
|
|
489
|
+
{ productId: testProducts[0].id, quantity: 2 },
|
|
490
|
+
{ productId: testProducts[1].id, quantity: 1 },
|
|
491
|
+
],
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
expect(status).toBe(201);
|
|
496
|
+
expect(body.userId).toBe(testUser.id);
|
|
497
|
+
expect(body.items).toHaveLength(2);
|
|
498
|
+
expect(body.total).toBe(2 * 29.99 + 49.99);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test('should list user orders', async ({ apiRequest }) => {
|
|
502
|
+
// Create an order first
|
|
503
|
+
await apiRequest({
|
|
504
|
+
method: 'POST',
|
|
505
|
+
path: '/api/orders',
|
|
506
|
+
body: {
|
|
507
|
+
userId: testUser.id,
|
|
508
|
+
items: [{ productId: testProducts[2].id, quantity: 1 }],
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// List orders for user
|
|
513
|
+
const { status, body } = await apiRequest({
|
|
514
|
+
method: 'GET',
|
|
515
|
+
path: '/api/orders',
|
|
516
|
+
params: { userId: testUser.id },
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
expect(status).toBe(200);
|
|
520
|
+
expect(body.orders.length).toBeGreaterThanOrEqual(1);
|
|
521
|
+
expect(body.orders.every((o: any) => o.userId === testUser.id)).toBe(true);
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Key Points**:
|
|
527
|
+
|
|
528
|
+
- `beforeAll`/`afterAll` for test data setup/cleanup
|
|
529
|
+
- API-based seeding (no direct DB access needed)
|
|
530
|
+
- Unique emails to prevent conflicts in parallel runs
|
|
531
|
+
- Cleanup after all tests complete
|
|
532
|
+
|
|
533
|
+
### Example 6: Background Job Testing with Recurse
|
|
534
|
+
|
|
535
|
+
**Context**: Test async operations like background jobs, webhooks, and eventual consistency.
|
|
536
|
+
|
|
537
|
+
**Implementation**:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// tests/api/background-jobs.spec.ts
|
|
541
|
+
import { test, expect } from '@seontechnologies/playwright-utils/fixtures';
|
|
542
|
+
|
|
543
|
+
test.describe('Background Jobs', () => {
|
|
544
|
+
test('should process export job', async ({ apiRequest, recurse }) => {
|
|
545
|
+
// Trigger export job
|
|
546
|
+
const { body: job } = await apiRequest({
|
|
547
|
+
method: 'POST',
|
|
548
|
+
path: '/api/exports',
|
|
549
|
+
body: {
|
|
550
|
+
type: 'users',
|
|
551
|
+
format: 'csv',
|
|
552
|
+
filters: { createdAfter: '2024-01-01' },
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
expect(job.id).toBeDefined();
|
|
557
|
+
expect(job.status).toBe('pending');
|
|
558
|
+
|
|
559
|
+
// Poll until job completes
|
|
560
|
+
const { body: completedJob } = await recurse(
|
|
561
|
+
() => apiRequest({ method: 'GET', path: `/api/exports/${job.id}` }),
|
|
562
|
+
(response) => response.body.status === 'completed',
|
|
563
|
+
{
|
|
564
|
+
timeout: 60000,
|
|
565
|
+
interval: 2000,
|
|
566
|
+
log: `Waiting for export job ${job.id} to complete`,
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
expect(completedJob.status).toBe('completed');
|
|
571
|
+
expect(completedJob.downloadUrl).toBeDefined();
|
|
572
|
+
expect(completedJob.recordCount).toBeGreaterThan(0);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test('should handle job failure gracefully', async ({ apiRequest, recurse }) => {
|
|
576
|
+
// Trigger job that will fail
|
|
577
|
+
const { body: job } = await apiRequest({
|
|
578
|
+
method: 'POST',
|
|
579
|
+
path: '/api/exports',
|
|
580
|
+
body: {
|
|
581
|
+
type: 'invalid-type', // This will cause failure
|
|
582
|
+
format: 'csv',
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// Poll until job fails
|
|
587
|
+
const { body: failedJob } = await recurse(
|
|
588
|
+
() => apiRequest({ method: 'GET', path: `/api/exports/${job.id}` }),
|
|
589
|
+
(response) => ['completed', 'failed'].includes(response.body.status),
|
|
590
|
+
{ timeout: 30000 }
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
expect(failedJob.status).toBe('failed');
|
|
594
|
+
expect(failedJob.error).toBeDefined();
|
|
595
|
+
expect(failedJob.error.code).toBe('INVALID_EXPORT_TYPE');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test('should process webhook delivery', async ({ apiRequest, recurse }) => {
|
|
599
|
+
// Trigger action that sends webhook
|
|
600
|
+
const { body: order } = await apiRequest({
|
|
601
|
+
method: 'POST',
|
|
602
|
+
path: '/api/orders',
|
|
603
|
+
body: {
|
|
604
|
+
userId: 'user-123',
|
|
605
|
+
items: [{ productId: 'prod-1', quantity: 1 }],
|
|
606
|
+
webhookUrl: 'https://webhook.site/test-endpoint',
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// Poll for webhook delivery status
|
|
611
|
+
const { body: webhookStatus } = await recurse(
|
|
612
|
+
() => apiRequest({ method: 'GET', path: `/api/webhooks/order/${order.id}` }),
|
|
613
|
+
(response) => response.body.delivered === true,
|
|
614
|
+
{ timeout: 30000, interval: 1000 }
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
expect(webhookStatus.delivered).toBe(true);
|
|
618
|
+
expect(webhookStatus.deliveredAt).toBeDefined();
|
|
619
|
+
expect(webhookStatus.responseStatus).toBe(200);
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**Key Points**:
|
|
625
|
+
|
|
626
|
+
- `recurse` for polling async operations
|
|
627
|
+
- Test both success and failure scenarios
|
|
628
|
+
- Configurable timeout and interval
|
|
629
|
+
- Log messages for debugging
|
|
630
|
+
|
|
631
|
+
### Example 7: Service Authentication (No Browser)
|
|
632
|
+
|
|
633
|
+
**Context**: Test authenticated API endpoints using tokens directly - no browser login needed.
|
|
634
|
+
|
|
635
|
+
**Implementation**:
|
|
636
|
+
|
|
637
|
+
```typescript
|
|
638
|
+
// tests/api/authenticated.spec.ts
|
|
639
|
+
import { test, expect } from '@seontechnologies/playwright-utils/fixtures';
|
|
640
|
+
|
|
641
|
+
test.describe('Authenticated API Tests', () => {
|
|
642
|
+
let authToken: string;
|
|
643
|
+
|
|
644
|
+
test.beforeAll(async ({ request }) => {
|
|
645
|
+
// Get token via API (no browser!)
|
|
646
|
+
const response = await request.post('/api/auth/login', {
|
|
647
|
+
data: {
|
|
648
|
+
email: process.env.TEST_USER_EMAIL,
|
|
649
|
+
password: process.env.TEST_USER_PASSWORD,
|
|
650
|
+
},
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
const { token } = await response.json();
|
|
654
|
+
authToken = token;
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
test('should access protected endpoint with token', async ({ apiRequest }) => {
|
|
658
|
+
const { status, body } = await apiRequest({
|
|
659
|
+
method: 'GET',
|
|
660
|
+
path: '/api/me',
|
|
661
|
+
headers: {
|
|
662
|
+
Authorization: `Bearer ${authToken}`,
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
expect(status).toBe(200);
|
|
667
|
+
expect(body.email).toBe(process.env.TEST_USER_EMAIL);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test('should reject request without token', async ({ apiRequest }) => {
|
|
671
|
+
const { status, body } = await apiRequest({
|
|
672
|
+
method: 'GET',
|
|
673
|
+
path: '/api/me',
|
|
674
|
+
// No Authorization header
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
expect(status).toBe(401);
|
|
678
|
+
expect(body.code).toBe('UNAUTHORIZED');
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
test('should reject expired token', async ({ apiRequest }) => {
|
|
682
|
+
const expiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Expired token
|
|
683
|
+
|
|
684
|
+
const { status, body } = await apiRequest({
|
|
685
|
+
method: 'GET',
|
|
686
|
+
path: '/api/me',
|
|
687
|
+
headers: {
|
|
688
|
+
Authorization: `Bearer ${expiredToken}`,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
expect(status).toBe(401);
|
|
693
|
+
expect(body.code).toBe('TOKEN_EXPIRED');
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test('should handle role-based access', async ({ apiRequest }) => {
|
|
697
|
+
// User token (non-admin)
|
|
698
|
+
const { status } = await apiRequest({
|
|
699
|
+
method: 'GET',
|
|
700
|
+
path: '/api/admin/users',
|
|
701
|
+
headers: {
|
|
702
|
+
Authorization: `Bearer ${authToken}`,
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
expect(status).toBe(403); // Forbidden for non-admin
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Key Points**:
|
|
712
|
+
|
|
713
|
+
- Token obtained via API login (no browser)
|
|
714
|
+
- Token reused across all tests in describe block
|
|
715
|
+
- Test auth, expired tokens, and RBAC
|
|
716
|
+
- Pure API testing without UI
|
|
717
|
+
|
|
718
|
+
## API Test Configuration
|
|
719
|
+
|
|
720
|
+
### Playwright Config for API-Only Tests
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
// playwright.config.ts
|
|
724
|
+
import { defineConfig } from '@playwright/test';
|
|
725
|
+
|
|
726
|
+
export default defineConfig({
|
|
727
|
+
testDir: './tests/api',
|
|
728
|
+
|
|
729
|
+
// No browser needed for API tests
|
|
730
|
+
use: {
|
|
731
|
+
baseURL: process.env.API_URL || 'http://localhost:3000',
|
|
732
|
+
extraHTTPHeaders: {
|
|
733
|
+
'Accept': 'application/json',
|
|
734
|
+
'Content-Type': 'application/json',
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
|
|
738
|
+
// Faster without browser overhead
|
|
739
|
+
timeout: 30000,
|
|
740
|
+
|
|
741
|
+
// Run API tests in parallel
|
|
742
|
+
workers: 4,
|
|
743
|
+
fullyParallel: true,
|
|
744
|
+
|
|
745
|
+
// No screenshots/traces needed for API tests
|
|
746
|
+
reporter: [['html'], ['json', { outputFile: 'api-test-results.json' }]],
|
|
747
|
+
});
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### Separate API Test Project
|
|
751
|
+
|
|
752
|
+
```typescript
|
|
753
|
+
// playwright.config.ts
|
|
754
|
+
export default defineConfig({
|
|
755
|
+
projects: [
|
|
756
|
+
{
|
|
757
|
+
name: 'api',
|
|
758
|
+
testDir: './tests/api',
|
|
759
|
+
use: {
|
|
760
|
+
baseURL: process.env.API_URL,
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
name: 'e2e',
|
|
765
|
+
testDir: './tests/e2e',
|
|
766
|
+
use: {
|
|
767
|
+
baseURL: process.env.APP_URL,
|
|
768
|
+
...devices['Desktop Chrome'],
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
],
|
|
772
|
+
});
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
## Comparison: API Tests vs E2E Tests
|
|
776
|
+
|
|
777
|
+
| Aspect | API Test | E2E Test |
|
|
778
|
+
|--------|----------|----------|
|
|
779
|
+
| **Speed** | ~50-100ms per test | ~2-10s per test |
|
|
780
|
+
| **Stability** | Very stable | More flaky (UI timing) |
|
|
781
|
+
| **Setup** | Minimal | Browser, context, page |
|
|
782
|
+
| **Debugging** | Clear request/response | DOM, screenshots, traces |
|
|
783
|
+
| **Coverage** | Service logic | User experience |
|
|
784
|
+
| **Parallelization** | Easy (stateless) | Complex (browser resources) |
|
|
785
|
+
| **CI Cost** | Low (no browser) | High (browser containers) |
|
|
786
|
+
|
|
787
|
+
## Related Fragments
|
|
788
|
+
|
|
789
|
+
- `api-request.md` - apiRequest utility details
|
|
790
|
+
- `recurse.md` - Polling patterns for async operations
|
|
791
|
+
- `auth-session.md` - Token management
|
|
792
|
+
- `contract-testing.md` - Pact contract testing
|
|
793
|
+
- `test-levels-framework.md` - When to use which test level
|
|
794
|
+
- `data-factories.md` - Test data setup patterns
|
|
795
|
+
|
|
796
|
+
## Anti-Patterns
|
|
797
|
+
|
|
798
|
+
**DON'T use E2E for API validation:**
|
|
799
|
+
|
|
800
|
+
```typescript
|
|
801
|
+
// Bad: Testing API through UI
|
|
802
|
+
test('validate user creation', async ({ page }) => {
|
|
803
|
+
await page.goto('/admin/users');
|
|
804
|
+
await page.fill('#name', 'John');
|
|
805
|
+
await page.click('#submit');
|
|
806
|
+
await expect(page.getByText('User created')).toBeVisible();
|
|
807
|
+
});
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
**DO test APIs directly:**
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
// Good: Direct API test
|
|
814
|
+
test('validate user creation', async ({ apiRequest }) => {
|
|
815
|
+
const { status, body } = await apiRequest({
|
|
816
|
+
method: 'POST',
|
|
817
|
+
path: '/api/users',
|
|
818
|
+
body: { name: 'John' },
|
|
819
|
+
});
|
|
820
|
+
expect(status).toBe(201);
|
|
821
|
+
expect(body.id).toBeDefined();
|
|
822
|
+
});
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
**DON'T ignore API tests because "E2E covers it":**
|
|
826
|
+
|
|
827
|
+
```typescript
|
|
828
|
+
// Bad thinking: "Our E2E tests create users, so API is tested"
|
|
829
|
+
// Reality: E2E tests one happy path; API tests cover edge cases
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
**DO have dedicated API test coverage:**
|
|
833
|
+
|
|
834
|
+
```typescript
|
|
835
|
+
// Good: Explicit API test suite
|
|
836
|
+
test.describe('Users API', () => {
|
|
837
|
+
test('creates user', async ({ apiRequest }) => { /* ... */ });
|
|
838
|
+
test('handles duplicate email', async ({ apiRequest }) => { /* ... */ });
|
|
839
|
+
test('validates required fields', async ({ apiRequest }) => { /* ... */ });
|
|
840
|
+
test('handles malformed JSON', async ({ apiRequest }) => { /* ... */ });
|
|
841
|
+
test('rate limits requests', async ({ apiRequest }) => { /* ... */ });
|
|
842
|
+
});
|
|
843
|
+
```
|