@mytechtoday/augment-extensions 1.7.0 → 2.2.0
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/README.md +119 -8
- package/augment-extensions/coding-standards/bash/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/bash/metadata.json +21 -0
- package/augment-extensions/coding-standards/c/metadata.json +50 -0
- package/augment-extensions/coding-standards/css/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/css/metadata.json +30 -0
- package/augment-extensions/coding-standards/go/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/go/metadata.json +57 -0
- package/augment-extensions/coding-standards/html/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html/metadata.json +25 -0
- package/augment-extensions/coding-standards/html-css-js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html-css-js/metadata.json +40 -0
- package/augment-extensions/coding-standards/js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/js/metadata.json +37 -0
- package/augment-extensions/coding-standards/php/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/php/metadata.json +35 -0
- package/augment-extensions/coding-standards/powershell/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/powershell/metadata.json +33 -0
- package/augment-extensions/coding-standards/python/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/python/metadata.json +38 -0
- package/augment-extensions/coding-standards/react/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/react/metadata.json +31 -0
- package/augment-extensions/coding-standards/typescript/CHANGELOG.md +39 -0
- package/augment-extensions/coding-standards/typescript/README.md +248 -19
- package/augment-extensions/coding-standards/typescript/VERSION +2 -0
- package/augment-extensions/coding-standards/typescript/examples/biome.json +199 -0
- package/augment-extensions/coding-standards/typescript/examples/eslint-flat-config.js +166 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/README.md +47 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/package.json +37 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/pnpm-workspace.yaml +5 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/tsconfig.base.json +50 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/turbo.json +47 -0
- package/augment-extensions/coding-standards/typescript/examples/vitest-setup.ts +198 -0
- package/augment-extensions/coding-standards/typescript/metadata.json +46 -0
- package/augment-extensions/coding-standards/typescript/module.json +25 -7
- package/augment-extensions/coding-standards/typescript/rules/architecture.md +1096 -0
- package/augment-extensions/coding-standards/typescript/rules/error-handling.md +1174 -0
- package/augment-extensions/coding-standards/typescript/rules/modern-features.md +625 -0
- package/augment-extensions/coding-standards/typescript/rules/monorepo.md +745 -0
- package/augment-extensions/coding-standards/typescript/rules/security-performance.md +850 -0
- package/augment-extensions/coding-standards/typescript/rules/testing.md +918 -0
- package/augment-extensions/coding-standards/typescript/rules/tooling.md +1071 -0
- package/augment-extensions/coding-standards/typescript/rules/type-patterns.md +750 -0
- package/augment-extensions/domain-rules/api-design/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/api-design/metadata.json +32 -0
- package/augment-extensions/domain-rules/database/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/database/metadata.json +50 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/metadata.json +18 -0
- package/augment-extensions/domain-rules/mcp/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/mcp/metadata.json +42 -0
- package/augment-extensions/domain-rules/security/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/security/metadata.json +33 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/metadata.json +40 -0
- package/augment-extensions/domain-rules/software-architecture/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/software-architecture/metadata.json +55 -0
- package/augment-extensions/domain-rules/wordpress/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress/metadata.json +42 -0
- package/augment-extensions/domain-rules/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress-plugin/metadata.json +70 -0
- package/augment-extensions/examples/design-patterns/CHANGELOG.md +51 -0
- package/augment-extensions/examples/design-patterns/metadata.json +29 -0
- package/augment-extensions/examples/gutenberg-block-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/gutenberg-block-plugin/metadata.json +26 -0
- package/augment-extensions/examples/rest-api-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/rest-api-plugin/metadata.json +27 -0
- package/augment-extensions/examples/woocommerce-extension/CHANGELOG.md +51 -0
- package/augment-extensions/examples/woocommerce-extension/metadata.json +27 -0
- package/augment-extensions/visual-design/metadata.json +34 -0
- package/augment-extensions/workflows/adr-support/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/adr-support/metadata.json +41 -0
- package/augment-extensions/workflows/beads/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads/metadata.json +23 -0
- package/augment-extensions/workflows/beads-integration/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads-integration/metadata.json +30 -0
- package/augment-extensions/workflows/database/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/database/metadata.json +30 -0
- package/augment-extensions/workflows/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/wordpress-plugin/metadata.json +41 -0
- package/augment-extensions/writing-standards/literature/shakespeare/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/literature/shakespeare/metadata.json +60 -0
- package/augment-extensions/writing-standards/screenplay/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/metadata.json +33 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/metadata.json +50 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/metadata.json +23 -0
- package/augment-extensions/writing-standards/screenplay/genres/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/genres/metadata.json +43 -0
- package/augment-extensions/writing-standards/screenplay/metadata.json +65 -0
- package/augment-extensions/writing-standards/screenplay/styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/styles/metadata.json +38 -0
- package/augment-extensions/writing-standards/screenplay/themes/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/themes/metadata.json +47 -0
- package/cli/dist/__test_health-checker.d.ts +2 -0
- package/cli/dist/__test_health-checker.d.ts.map +1 -0
- package/cli/dist/__test_health-checker.js +4 -0
- package/cli/dist/__test_health-checker.js.map +1 -0
- package/cli/dist/__test_template-engine.d.ts +2 -0
- package/cli/dist/__test_template-engine.d.ts.map +1 -0
- package/cli/dist/__test_template-engine.js +4 -0
- package/cli/dist/__test_template-engine.js.map +1 -0
- package/cli/dist/cli.js +25 -0
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts +8 -2
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.js +28 -3
- package/cli/dist/commands/generate-shot-list/generator/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts +3 -0
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.js +48 -5
- package/cli/dist/commands/generate-shot-list/help-text.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts +11 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js +61 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts +29 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js +111 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts +18 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js +184 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts +4 -3
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/index.js +54 -3
- package/cli/dist/commands/generate-shot-list/parser/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts +24 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js +84 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts +28 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js +143 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts +9 -6
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/types.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js +108 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts +39 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.js +85 -0
- package/cli/dist/commands/generate-shot-list/style/index.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts +35 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js +178 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js +123 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts +105 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.js +8 -0
- package/cli/dist/commands/generate-shot-list/style/types.js.map +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list.js +44 -1
- package/cli/dist/commands/generate-shot-list.js.map +1 -1
- package/cli/dist/commands/list.d.ts +1 -0
- package/cli/dist/commands/list.d.ts.map +1 -1
- package/cli/dist/commands/list.js +21 -5
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/upgrade.d.ts +15 -0
- package/cli/dist/commands/upgrade.d.ts.map +1 -0
- package/cli/dist/commands/upgrade.js +229 -0
- package/cli/dist/commands/upgrade.js.map +1 -0
- package/cli/dist/commands/use.d.ts +15 -0
- package/cli/dist/commands/use.d.ts.map +1 -0
- package/cli/dist/commands/use.js +180 -0
- package/cli/dist/commands/use.js.map +1 -0
- package/cli/dist/commands/version-info.d.ts +15 -0
- package/cli/dist/commands/version-info.d.ts.map +1 -0
- package/cli/dist/commands/version-info.js +194 -0
- package/cli/dist/commands/version-info.js.map +1 -0
- package/cli/dist/core/compatibility-checker.d.ts +73 -0
- package/cli/dist/core/compatibility-checker.d.ts.map +1 -0
- package/cli/dist/core/compatibility-checker.js +188 -0
- package/cli/dist/core/compatibility-checker.js.map +1 -0
- package/cli/dist/core/index.d.ts +9 -0
- package/cli/dist/core/index.d.ts.map +1 -0
- package/cli/dist/core/index.js +16 -0
- package/cli/dist/core/index.js.map +1 -0
- package/cli/dist/core/module-loader.d.ts +68 -0
- package/cli/dist/core/module-loader.d.ts.map +1 -0
- package/cli/dist/core/module-loader.js +137 -0
- package/cli/dist/core/module-loader.js.map +1 -0
- package/cli/dist/core/version-manager.d.ts +62 -0
- package/cli/dist/core/version-manager.d.ts.map +1 -0
- package/cli/dist/core/version-manager.js +157 -0
- package/cli/dist/core/version-manager.js.map +1 -0
- package/cli/dist/core/version-resolver.d.ts +63 -0
- package/cli/dist/core/version-resolver.d.ts.map +1 -0
- package/cli/dist/core/version-resolver.js +163 -0
- package/cli/dist/core/version-resolver.js.map +1 -0
- package/cli/dist/gui/app.d.ts +17 -0
- package/cli/dist/gui/app.d.ts.map +1 -0
- package/cli/dist/gui/app.js +79 -0
- package/cli/dist/gui/app.js.map +1 -0
- package/cli/dist/gui/components/preview-pane.d.ts +40 -0
- package/cli/dist/gui/components/preview-pane.d.ts.map +1 -0
- package/cli/dist/gui/components/preview-pane.js +90 -0
- package/cli/dist/gui/components/preview-pane.js.map +1 -0
- package/cli/dist/gui/components/search-filter.d.ts +26 -0
- package/cli/dist/gui/components/search-filter.d.ts.map +1 -0
- package/cli/dist/gui/components/search-filter.js +168 -0
- package/cli/dist/gui/components/search-filter.js.map +1 -0
- package/cli/dist/gui/components/status-bar.d.ts +22 -0
- package/cli/dist/gui/components/status-bar.d.ts.map +1 -0
- package/cli/dist/gui/components/status-bar.js +58 -0
- package/cli/dist/gui/components/status-bar.js.map +1 -0
- package/cli/dist/gui/components/tree-navigator.d.ts +30 -0
- package/cli/dist/gui/components/tree-navigator.d.ts.map +1 -0
- package/cli/dist/gui/components/tree-navigator.js +133 -0
- package/cli/dist/gui/components/tree-navigator.js.map +1 -0
- package/cli/dist/gui/components/version-selector.d.ts +27 -0
- package/cli/dist/gui/components/version-selector.d.ts.map +1 -0
- package/cli/dist/gui/components/version-selector.js +110 -0
- package/cli/dist/gui/components/version-selector.js.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts +25 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.js +116 -0
- package/cli/dist/gui/hooks/use-version-loader.js.map +1 -0
- package/cli/dist/gui/index.d.ts +15 -0
- package/cli/dist/gui/index.d.ts.map +1 -0
- package/cli/dist/gui/index.js +39 -0
- package/cli/dist/gui/index.js.map +1 -0
- package/cli/dist/gui/layouts/main-layout.d.ts +14 -0
- package/cli/dist/gui/layouts/main-layout.d.ts.map +1 -0
- package/cli/dist/gui/layouts/main-layout.js +205 -0
- package/cli/dist/gui/layouts/main-layout.js.map +1 -0
- package/cli/dist/gui/state/navigation-state.d.ts +39 -0
- package/cli/dist/gui/state/navigation-state.d.ts.map +1 -0
- package/cli/dist/gui/state/navigation-state.js +151 -0
- package/cli/dist/gui/state/navigation-state.js.map +1 -0
- package/cli/dist/gui/state/selection-state.d.ts +36 -0
- package/cli/dist/gui/state/selection-state.d.ts.map +1 -0
- package/cli/dist/gui/state/selection-state.js +132 -0
- package/cli/dist/gui/state/selection-state.js.map +1 -0
- package/cli/dist/gui/theme.d.ts +50 -0
- package/cli/dist/gui/theme.d.ts.map +1 -0
- package/cli/dist/gui/theme.js +73 -0
- package/cli/dist/gui/theme.js.map +1 -0
- package/cli/dist/utils/help.d.ts +13 -0
- package/cli/dist/utils/help.d.ts.map +1 -0
- package/cli/dist/utils/help.js +137 -0
- package/cli/dist/utils/help.js.map +1 -0
- package/cli/dist/utils/module-system.d.ts +8 -0
- package/cli/dist/utils/module-system.d.ts.map +1 -1
- package/cli/dist/utils/module-system.js +38 -3
- package/cli/dist/utils/module-system.js.map +1 -1
- package/cli/dist/utils/validate-versioning.d.ts +43 -0
- package/cli/dist/utils/validate-versioning.d.ts.map +1 -0
- package/cli/dist/utils/validate-versioning.js +258 -0
- package/cli/dist/utils/validate-versioning.js.map +1 -0
- package/package.json +10 -1
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
# TypeScript Testing Strategies
|
|
2
|
+
|
|
3
|
+
Comprehensive guide to testing TypeScript applications covering unit, integration, and end-to-end testing patterns.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Testing Pyramid](#testing-pyramid)
|
|
7
|
+
- [Unit Testing](#unit-testing)
|
|
8
|
+
- [Integration Testing](#integration-testing)
|
|
9
|
+
- [End-to-End Testing](#end-to-end-testing)
|
|
10
|
+
- [Snapshot vs Assertion Testing](#snapshot-vs-assertion-testing)
|
|
11
|
+
- [Property-Based Testing](#property-based-testing)
|
|
12
|
+
- [Test Organization Patterns](#test-organization-patterns)
|
|
13
|
+
- [MSW for API Mocking](#msw-for-api-mocking)
|
|
14
|
+
- [Best Practices](#best-practices)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Testing Pyramid
|
|
19
|
+
|
|
20
|
+
The testing pyramid guides test distribution:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
/\
|
|
24
|
+
/E2E\ ← Few (5-10%)
|
|
25
|
+
/------\
|
|
26
|
+
/ INT \ ← Some (20-30%)
|
|
27
|
+
/----------\
|
|
28
|
+
/ UNIT \ ← Many (60-75%)
|
|
29
|
+
/--------------\
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Principles:**
|
|
33
|
+
- **Unit tests**: Fast, isolated, many
|
|
34
|
+
- **Integration tests**: Medium speed, test component interactions
|
|
35
|
+
- **E2E tests**: Slow, expensive, critical user flows only
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Unit Testing
|
|
40
|
+
|
|
41
|
+
### Basic Unit Test Structure
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// user-service.test.ts
|
|
45
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
46
|
+
import { UserService } from './user-service';
|
|
47
|
+
import type { UserRepository } from './user-repository';
|
|
48
|
+
|
|
49
|
+
describe('UserService', () => {
|
|
50
|
+
let userService: UserService;
|
|
51
|
+
let mockRepository: UserRepository;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
// Setup fresh mocks for each test
|
|
55
|
+
mockRepository = {
|
|
56
|
+
findById: vi.fn(),
|
|
57
|
+
save: vi.fn(),
|
|
58
|
+
delete: vi.fn()
|
|
59
|
+
};
|
|
60
|
+
userService = new UserService(mockRepository);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('getUser', () => {
|
|
64
|
+
it('should return user when found', async () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
const mockUser = { id: '1', name: 'John Doe', email: 'john@example.com' };
|
|
67
|
+
mockRepository.findById.mockResolvedValue(mockUser);
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
const result = await userService.getUser('1');
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(result).toEqual(mockUser);
|
|
74
|
+
expect(mockRepository.findById).toHaveBeenCalledWith('1');
|
|
75
|
+
expect(mockRepository.findById).toHaveBeenCalledTimes(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should throw error when user not found', async () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
mockRepository.findById.mockResolvedValue(null);
|
|
81
|
+
|
|
82
|
+
// Act & Assert
|
|
83
|
+
await expect(userService.getUser('999')).rejects.toThrow('User not found');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Testing Async Code
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// async-operations.test.ts
|
|
93
|
+
import { describe, it, expect } from 'vitest';
|
|
94
|
+
import { fetchUserData, retryOperation } from './async-operations';
|
|
95
|
+
|
|
96
|
+
describe('Async Operations', () => {
|
|
97
|
+
it('should handle async/await', async () => {
|
|
98
|
+
const data = await fetchUserData('123');
|
|
99
|
+
expect(data).toBeDefined();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle promise rejection', async () => {
|
|
103
|
+
await expect(fetchUserData('invalid')).rejects.toThrow('Invalid user ID');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should retry failed operations', async () => {
|
|
107
|
+
let attempts = 0;
|
|
108
|
+
const operation = vi.fn(async () => {
|
|
109
|
+
attempts++;
|
|
110
|
+
if (attempts < 3) throw new Error('Temporary failure');
|
|
111
|
+
return 'success';
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const result = await retryOperation(operation, { maxRetries: 3 });
|
|
115
|
+
expect(result).toBe('success');
|
|
116
|
+
expect(operation).toHaveBeenCalledTimes(3);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Testing React Components
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// button.test.tsx
|
|
125
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
126
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
127
|
+
import { Button } from './button';
|
|
128
|
+
|
|
129
|
+
describe('Button', () => {
|
|
130
|
+
it('should render with text', () => {
|
|
131
|
+
render(<Button>Click me</Button>);
|
|
132
|
+
expect(screen.getByRole('button')).toHaveTextContent('Click me');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should call onClick when clicked', () => {
|
|
136
|
+
const handleClick = vi.fn();
|
|
137
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
138
|
+
|
|
139
|
+
fireEvent.click(screen.getByRole('button'));
|
|
140
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should be disabled when disabled prop is true', () => {
|
|
144
|
+
render(<Button disabled>Click me</Button>);
|
|
145
|
+
expect(screen.getByRole('button')).toBeDisabled();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should apply custom className', () => {
|
|
149
|
+
render(<Button className="custom-class">Click me</Button>);
|
|
150
|
+
expect(screen.getByRole('button')).toHaveClass('custom-class');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Testing Database Integration
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// user-repository.integration.test.ts
|
|
159
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
160
|
+
import { UserRepository } from './user-repository';
|
|
161
|
+
import { createTestDatabase, cleanupDatabase } from '../test-utils/db';
|
|
162
|
+
|
|
163
|
+
describe('UserRepository Integration', () => {
|
|
164
|
+
let repository: UserRepository;
|
|
165
|
+
let db: Database;
|
|
166
|
+
|
|
167
|
+
beforeEach(async () => {
|
|
168
|
+
db = await createTestDatabase();
|
|
169
|
+
repository = new UserRepository(db);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
afterEach(async () => {
|
|
173
|
+
await cleanupDatabase(db);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should save and retrieve user', async () => {
|
|
177
|
+
const user = { name: 'John Doe', email: 'john@example.com' };
|
|
178
|
+
|
|
179
|
+
const saved = await repository.save(user);
|
|
180
|
+
expect(saved.id).toBeDefined();
|
|
181
|
+
|
|
182
|
+
const retrieved = await repository.findById(saved.id);
|
|
183
|
+
expect(retrieved).toEqual(saved);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should update existing user', async () => {
|
|
187
|
+
const user = await repository.save({ name: 'John', email: 'john@example.com' });
|
|
188
|
+
|
|
189
|
+
const updated = await repository.save({ ...user, name: 'Jane' });
|
|
190
|
+
expect(updated.name).toBe('Jane');
|
|
191
|
+
expect(updated.id).toBe(user.id);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## End-to-End Testing
|
|
199
|
+
|
|
200
|
+
### Playwright E2E Tests
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// login.e2e.test.ts
|
|
204
|
+
import { test, expect } from '@playwright/test';
|
|
205
|
+
|
|
206
|
+
test.describe('Login Flow', () => {
|
|
207
|
+
test('should login successfully with valid credentials', async ({ page }) => {
|
|
208
|
+
await page.goto('/login');
|
|
209
|
+
|
|
210
|
+
await page.fill('input[name="email"]', 'user@example.com');
|
|
211
|
+
await page.fill('input[name="password"]', 'password123');
|
|
212
|
+
await page.click('button[type="submit"]');
|
|
213
|
+
|
|
214
|
+
await expect(page).toHaveURL('/dashboard');
|
|
215
|
+
await expect(page.locator('h1')).toContainText('Welcome');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('should show error with invalid credentials', async ({ page }) => {
|
|
219
|
+
await page.goto('/login');
|
|
220
|
+
|
|
221
|
+
await page.fill('input[name="email"]', 'wrong@example.com');
|
|
222
|
+
await page.fill('input[name="password"]', 'wrongpass');
|
|
223
|
+
await page.click('button[type="submit"]');
|
|
224
|
+
|
|
225
|
+
await expect(page.locator('.error-message')).toContainText('Invalid credentials');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Cypress E2E Tests
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
// checkout.cy.ts
|
|
234
|
+
describe('Checkout Flow', () => {
|
|
235
|
+
beforeEach(() => {
|
|
236
|
+
cy.visit('/products');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should complete purchase', () => {
|
|
240
|
+
// Add item to cart
|
|
241
|
+
cy.get('[data-testid="product-1"]').click();
|
|
242
|
+
cy.get('[data-testid="add-to-cart"]').click();
|
|
243
|
+
|
|
244
|
+
// Go to checkout
|
|
245
|
+
cy.get('[data-testid="cart-icon"]').click();
|
|
246
|
+
cy.get('[data-testid="checkout-button"]').click();
|
|
247
|
+
|
|
248
|
+
// Fill shipping info
|
|
249
|
+
cy.get('input[name="address"]').type('123 Main St');
|
|
250
|
+
cy.get('input[name="city"]').type('New York');
|
|
251
|
+
cy.get('input[name="zip"]').type('10001');
|
|
252
|
+
|
|
253
|
+
// Submit order
|
|
254
|
+
cy.get('button[type="submit"]').click();
|
|
255
|
+
|
|
256
|
+
// Verify success
|
|
257
|
+
cy.url().should('include', '/order-confirmation');
|
|
258
|
+
cy.contains('Order placed successfully').should('be.visible');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Snapshot vs Assertion Testing
|
|
266
|
+
|
|
267
|
+
### Snapshot Testing
|
|
268
|
+
|
|
269
|
+
**Use for:**
|
|
270
|
+
- Component rendering output
|
|
271
|
+
- API response structures
|
|
272
|
+
- Configuration objects
|
|
273
|
+
- Error messages
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// component-snapshot.test.tsx
|
|
277
|
+
import { describe, it, expect } from 'vitest';
|
|
278
|
+
import { render } from '@testing-library/react';
|
|
279
|
+
import { UserCard } from './user-card';
|
|
280
|
+
|
|
281
|
+
describe('UserCard Snapshots', () => {
|
|
282
|
+
it('should match snapshot', () => {
|
|
283
|
+
const { container } = render(
|
|
284
|
+
<UserCard
|
|
285
|
+
name="John Doe"
|
|
286
|
+
email="john@example.com"
|
|
287
|
+
role="admin"
|
|
288
|
+
/>
|
|
289
|
+
);
|
|
290
|
+
expect(container).toMatchSnapshot();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should match inline snapshot', () => {
|
|
294
|
+
const config = {
|
|
295
|
+
apiUrl: 'https://api.example.com',
|
|
296
|
+
timeout: 5000,
|
|
297
|
+
retries: 3
|
|
298
|
+
};
|
|
299
|
+
expect(config).toMatchInlineSnapshot(`
|
|
300
|
+
{
|
|
301
|
+
"apiUrl": "https://api.example.com",
|
|
302
|
+
"retries": 3,
|
|
303
|
+
"timeout": 5000,
|
|
304
|
+
}
|
|
305
|
+
`);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Assertion Testing
|
|
311
|
+
|
|
312
|
+
**Use for:**
|
|
313
|
+
- Business logic
|
|
314
|
+
- Calculations
|
|
315
|
+
- State changes
|
|
316
|
+
- Specific behaviors
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// assertion-testing.test.ts
|
|
320
|
+
import { describe, it, expect } from 'vitest';
|
|
321
|
+
import { calculateDiscount, validateEmail } from './utils';
|
|
322
|
+
|
|
323
|
+
describe('Assertion Testing', () => {
|
|
324
|
+
it('should calculate discount correctly', () => {
|
|
325
|
+
expect(calculateDiscount(100, 0.1)).toBe(10);
|
|
326
|
+
expect(calculateDiscount(50, 0.2)).toBe(10);
|
|
327
|
+
expect(calculateDiscount(0, 0.5)).toBe(0);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should validate email format', () => {
|
|
331
|
+
expect(validateEmail('user@example.com')).toBe(true);
|
|
332
|
+
expect(validateEmail('invalid-email')).toBe(false);
|
|
333
|
+
expect(validateEmail('')).toBe(false);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**When to use each:**
|
|
339
|
+
|
|
340
|
+
| Snapshot Testing | Assertion Testing |
|
|
341
|
+
|-----------------|-------------------|
|
|
342
|
+
| UI component output | Business logic |
|
|
343
|
+
| Large data structures | Specific values |
|
|
344
|
+
| Regression detection | Behavior verification |
|
|
345
|
+
| Quick to write | More explicit |
|
|
346
|
+
| Can be brittle | More maintainable |
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Property-Based Testing
|
|
351
|
+
|
|
352
|
+
Property-based testing generates random inputs to test invariants.
|
|
353
|
+
|
|
354
|
+
### Using fast-check
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// property-based.test.ts
|
|
358
|
+
import { describe, it, expect } from 'vitest';
|
|
359
|
+
import fc from 'fast-check';
|
|
360
|
+
import { sortArray, reverseString, addNumbers } from './utils';
|
|
361
|
+
|
|
362
|
+
describe('Property-Based Testing', () => {
|
|
363
|
+
it('sorting should be idempotent', () => {
|
|
364
|
+
fc.assert(
|
|
365
|
+
fc.property(fc.array(fc.integer()), (arr) => {
|
|
366
|
+
const sorted1 = sortArray(arr);
|
|
367
|
+
const sorted2 = sortArray(sorted1);
|
|
368
|
+
expect(sorted1).toEqual(sorted2);
|
|
369
|
+
})
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('reversing twice should return original', () => {
|
|
374
|
+
fc.assert(
|
|
375
|
+
fc.property(fc.string(), (str) => {
|
|
376
|
+
const reversed = reverseString(reverseString(str));
|
|
377
|
+
expect(reversed).toBe(str);
|
|
378
|
+
})
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('addition should be commutative', () => {
|
|
383
|
+
fc.assert(
|
|
384
|
+
fc.property(fc.integer(), fc.integer(), (a, b) => {
|
|
385
|
+
expect(addNumbers(a, b)).toBe(addNumbers(b, a));
|
|
386
|
+
})
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('array length should be preserved after mapping', () => {
|
|
391
|
+
fc.assert(
|
|
392
|
+
fc.property(fc.array(fc.anything()), (arr) => {
|
|
393
|
+
const mapped = arr.map(x => x);
|
|
394
|
+
expect(mapped.length).toBe(arr.length);
|
|
395
|
+
})
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Custom Generators
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// custom-generators.test.ts
|
|
405
|
+
import fc from 'fast-check';
|
|
406
|
+
|
|
407
|
+
// Custom user generator
|
|
408
|
+
const userArbitrary = fc.record({
|
|
409
|
+
id: fc.uuid(),
|
|
410
|
+
name: fc.string({ minLength: 1, maxLength: 50 }),
|
|
411
|
+
email: fc.emailAddress(),
|
|
412
|
+
age: fc.integer({ min: 18, max: 120 }),
|
|
413
|
+
role: fc.constantFrom('admin', 'user', 'guest')
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('User Validation', () => {
|
|
417
|
+
it('should validate all generated users', () => {
|
|
418
|
+
fc.assert(
|
|
419
|
+
fc.property(userArbitrary, (user) => {
|
|
420
|
+
expect(user.age).toBeGreaterThanOrEqual(18);
|
|
421
|
+
expect(user.email).toContain('@');
|
|
422
|
+
expect(['admin', 'user', 'guest']).toContain(user.role);
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Test Organization Patterns
|
|
432
|
+
|
|
433
|
+
### File Structure
|
|
434
|
+
|
|
435
|
+
```
|
|
436
|
+
src/
|
|
437
|
+
├── components/
|
|
438
|
+
│ ├── button/
|
|
439
|
+
│ │ ├── button.tsx
|
|
440
|
+
│ │ ├── button.test.tsx # Unit tests
|
|
441
|
+
│ │ └── button.stories.tsx # Storybook
|
|
442
|
+
│ └── user-profile/
|
|
443
|
+
│ ├── user-profile.tsx
|
|
444
|
+
│ ├── user-profile.test.tsx
|
|
445
|
+
│ └── user-profile.integration.test.tsx
|
|
446
|
+
├── services/
|
|
447
|
+
│ ├── user-service.ts
|
|
448
|
+
│ └── user-service.test.ts
|
|
449
|
+
└── __tests__/
|
|
450
|
+
├── integration/ # Integration tests
|
|
451
|
+
│ └── api.integration.test.ts
|
|
452
|
+
└── e2e/ # E2E tests
|
|
453
|
+
└── checkout.e2e.test.ts
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Test Naming Conventions
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// ✅ Good - Descriptive test names
|
|
460
|
+
describe('UserService', () => {
|
|
461
|
+
describe('getUser', () => {
|
|
462
|
+
it('should return user when found', () => {});
|
|
463
|
+
it('should throw UserNotFoundError when user does not exist', () => {});
|
|
464
|
+
it('should cache user data after first fetch', () => {});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
describe('createUser', () => {
|
|
468
|
+
it('should create user with valid data', () => {});
|
|
469
|
+
it('should throw ValidationError when email is invalid', () => {});
|
|
470
|
+
it('should hash password before saving', () => {});
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// ❌ Bad - Vague test names
|
|
475
|
+
describe('UserService', () => {
|
|
476
|
+
it('works', () => {});
|
|
477
|
+
it('test1', () => {});
|
|
478
|
+
it('should handle errors', () => {});
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Test Helpers and Utilities
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
// test-utils/factories.ts
|
|
486
|
+
export const createMockUser = (overrides?: Partial<User>): User => ({
|
|
487
|
+
id: '123',
|
|
488
|
+
name: 'Test User',
|
|
489
|
+
email: 'test@example.com',
|
|
490
|
+
role: 'user',
|
|
491
|
+
...overrides
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
export const createMockProduct = (overrides?: Partial<Product>): Product => ({
|
|
495
|
+
id: '456',
|
|
496
|
+
name: 'Test Product',
|
|
497
|
+
price: 99.99,
|
|
498
|
+
stock: 10,
|
|
499
|
+
...overrides
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// test-utils/render.tsx
|
|
503
|
+
import { render, RenderOptions } from '@testing-library/react';
|
|
504
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
505
|
+
|
|
506
|
+
export const renderWithProviders = (
|
|
507
|
+
ui: React.ReactElement,
|
|
508
|
+
options?: RenderOptions
|
|
509
|
+
) => {
|
|
510
|
+
const queryClient = new QueryClient({
|
|
511
|
+
defaultOptions: {
|
|
512
|
+
queries: { retry: false },
|
|
513
|
+
mutations: { retry: false }
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return render(
|
|
518
|
+
<QueryClientProvider client={queryClient}>
|
|
519
|
+
{ui}
|
|
520
|
+
</QueryClientProvider>,
|
|
521
|
+
options
|
|
522
|
+
);
|
|
523
|
+
};
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## MSW for API Mocking
|
|
529
|
+
|
|
530
|
+
Mock Service Worker (MSW) intercepts network requests for testing.
|
|
531
|
+
|
|
532
|
+
### Setup
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
// test-utils/msw-setup.ts
|
|
536
|
+
import { setupServer } from 'msw/node';
|
|
537
|
+
import { http, HttpResponse } from 'msw';
|
|
538
|
+
|
|
539
|
+
export const handlers = [
|
|
540
|
+
// GET request
|
|
541
|
+
http.get('/api/users/:id', ({ params }) => {
|
|
542
|
+
return HttpResponse.json({
|
|
543
|
+
id: params.id,
|
|
544
|
+
name: 'John Doe',
|
|
545
|
+
email: 'john@example.com'
|
|
546
|
+
});
|
|
547
|
+
}),
|
|
548
|
+
|
|
549
|
+
// POST request
|
|
550
|
+
http.post('/api/users', async ({ request }) => {
|
|
551
|
+
const body = await request.json();
|
|
552
|
+
return HttpResponse.json(
|
|
553
|
+
{ id: '123', ...body },
|
|
554
|
+
{ status: 201 }
|
|
555
|
+
);
|
|
556
|
+
}),
|
|
557
|
+
|
|
558
|
+
// Error response
|
|
559
|
+
http.get('/api/users/error', () => {
|
|
560
|
+
return HttpResponse.json(
|
|
561
|
+
{ error: 'User not found' },
|
|
562
|
+
{ status: 404 }
|
|
563
|
+
);
|
|
564
|
+
}),
|
|
565
|
+
|
|
566
|
+
// Delayed response
|
|
567
|
+
http.get('/api/slow', async () => {
|
|
568
|
+
await delay(1000);
|
|
569
|
+
return HttpResponse.json({ data: 'slow response' });
|
|
570
|
+
})
|
|
571
|
+
];
|
|
572
|
+
|
|
573
|
+
export const server = setupServer(...handlers);
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Test Setup
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
// vitest.setup.ts
|
|
580
|
+
import { beforeAll, afterEach, afterAll } from 'vitest';
|
|
581
|
+
import { server } from './test-utils/msw-setup';
|
|
582
|
+
|
|
583
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
|
584
|
+
afterEach(() => server.resetHandlers());
|
|
585
|
+
afterAll(() => server.close());
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Using MSW in Tests
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
// api-client.test.ts
|
|
592
|
+
import { describe, it, expect } from 'vitest';
|
|
593
|
+
import { server } from './test-utils/msw-setup';
|
|
594
|
+
import { http, HttpResponse } from 'msw';
|
|
595
|
+
import { fetchUser, createUser } from './api-client';
|
|
596
|
+
|
|
597
|
+
describe('API Client', () => {
|
|
598
|
+
it('should fetch user successfully', async () => {
|
|
599
|
+
const user = await fetchUser('123');
|
|
600
|
+
|
|
601
|
+
expect(user).toEqual({
|
|
602
|
+
id: '123',
|
|
603
|
+
name: 'John Doe',
|
|
604
|
+
email: 'john@example.com'
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('should handle 404 errors', async () => {
|
|
609
|
+
server.use(
|
|
610
|
+
http.get('/api/users/:id', () => {
|
|
611
|
+
return HttpResponse.json(
|
|
612
|
+
{ error: 'Not found' },
|
|
613
|
+
{ status: 404 }
|
|
614
|
+
);
|
|
615
|
+
})
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
await expect(fetchUser('999')).rejects.toThrow('Not found');
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it('should create user', async () => {
|
|
622
|
+
const newUser = { name: 'Jane Doe', email: 'jane@example.com' };
|
|
623
|
+
const created = await createUser(newUser);
|
|
624
|
+
|
|
625
|
+
expect(created).toMatchObject(newUser);
|
|
626
|
+
expect(created.id).toBeDefined();
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('should handle network errors', async () => {
|
|
630
|
+
server.use(
|
|
631
|
+
http.get('/api/users/:id', () => {
|
|
632
|
+
return HttpResponse.error();
|
|
633
|
+
})
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
await expect(fetchUser('123')).rejects.toThrow('Network error');
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### Advanced MSW Patterns
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
// Dynamic responses based on request
|
|
645
|
+
http.get('/api/search', ({ request }) => {
|
|
646
|
+
const url = new URL(request.url);
|
|
647
|
+
const query = url.searchParams.get('q');
|
|
648
|
+
|
|
649
|
+
if (!query) {
|
|
650
|
+
return HttpResponse.json({ results: [] });
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return HttpResponse.json({
|
|
654
|
+
results: mockSearchResults.filter(r =>
|
|
655
|
+
r.title.toLowerCase().includes(query.toLowerCase())
|
|
656
|
+
)
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// Stateful mocking
|
|
661
|
+
let users: User[] = [];
|
|
662
|
+
|
|
663
|
+
http.post('/api/users', async ({ request }) => {
|
|
664
|
+
const user = await request.json();
|
|
665
|
+
const newUser = { id: String(users.length + 1), ...user };
|
|
666
|
+
users.push(newUser);
|
|
667
|
+
return HttpResponse.json(newUser, { status: 201 });
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
http.get('/api/users', () => {
|
|
671
|
+
return HttpResponse.json(users);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Request validation
|
|
675
|
+
http.post('/api/users', async ({ request }) => {
|
|
676
|
+
const body = await request.json();
|
|
677
|
+
|
|
678
|
+
if (!body.email || !body.name) {
|
|
679
|
+
return HttpResponse.json(
|
|
680
|
+
{ error: 'Email and name are required' },
|
|
681
|
+
{ status: 400 }
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return HttpResponse.json({ id: '123', ...body }, { status: 201 });
|
|
686
|
+
});
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
## Best Practices
|
|
692
|
+
|
|
693
|
+
### DO ✅
|
|
694
|
+
|
|
695
|
+
**Write tests first (TDD)**
|
|
696
|
+
```typescript
|
|
697
|
+
// 1. Write failing test
|
|
698
|
+
it('should calculate total price', () => {
|
|
699
|
+
expect(calculateTotal([10, 20, 30])).toBe(60);
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
// 2. Implement minimal code to pass
|
|
703
|
+
function calculateTotal(prices: number[]): number {
|
|
704
|
+
return prices.reduce((sum, price) => sum + price, 0);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// 3. Refactor
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Use AAA pattern (Arrange, Act, Assert)**
|
|
711
|
+
```typescript
|
|
712
|
+
it('should add item to cart', () => {
|
|
713
|
+
// Arrange
|
|
714
|
+
const cart = new ShoppingCart();
|
|
715
|
+
const item = { id: '1', name: 'Product', price: 10 };
|
|
716
|
+
|
|
717
|
+
// Act
|
|
718
|
+
cart.addItem(item);
|
|
719
|
+
|
|
720
|
+
// Assert
|
|
721
|
+
expect(cart.items).toHaveLength(1);
|
|
722
|
+
expect(cart.total).toBe(10);
|
|
723
|
+
});
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
**Test behavior, not implementation**
|
|
727
|
+
```typescript
|
|
728
|
+
// ✅ Good - Tests behavior
|
|
729
|
+
it('should display user name after login', async () => {
|
|
730
|
+
render(<App />);
|
|
731
|
+
await userEvent.type(screen.getByLabelText('Email'), 'user@example.com');
|
|
732
|
+
await userEvent.click(screen.getByRole('button', { name: 'Login' }));
|
|
733
|
+
|
|
734
|
+
expect(screen.getByText('Welcome, John')).toBeInTheDocument();
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// ❌ Bad - Tests implementation
|
|
738
|
+
it('should call setState with user data', () => {
|
|
739
|
+
const setState = vi.fn();
|
|
740
|
+
// Testing internal implementation details
|
|
741
|
+
});
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**Keep tests independent**
|
|
745
|
+
```typescript
|
|
746
|
+
// ✅ Good - Each test is independent
|
|
747
|
+
describe('UserService', () => {
|
|
748
|
+
let service: UserService;
|
|
749
|
+
|
|
750
|
+
beforeEach(() => {
|
|
751
|
+
service = new UserService();
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it('test 1', () => { /* ... */ });
|
|
755
|
+
it('test 2', () => { /* ... */ });
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// ❌ Bad - Tests depend on each other
|
|
759
|
+
describe('UserService', () => {
|
|
760
|
+
let user: User;
|
|
761
|
+
|
|
762
|
+
it('should create user', () => {
|
|
763
|
+
user = service.create({ name: 'John' });
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it('should update user', () => {
|
|
767
|
+
service.update(user.id, { name: 'Jane' }); // Depends on previous test
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
**Use descriptive test names**
|
|
773
|
+
```typescript
|
|
774
|
+
// ✅ Good
|
|
775
|
+
it('should throw ValidationError when email is missing', () => {});
|
|
776
|
+
it('should return cached data on second request', () => {});
|
|
777
|
+
it('should retry failed requests up to 3 times', () => {});
|
|
778
|
+
|
|
779
|
+
// ❌ Bad
|
|
780
|
+
it('test email', () => {});
|
|
781
|
+
it('caching works', () => {});
|
|
782
|
+
it('retries', () => {});
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
### DON'T ❌
|
|
786
|
+
|
|
787
|
+
**Don't test external libraries**
|
|
788
|
+
```typescript
|
|
789
|
+
// ❌ Bad - Testing React itself
|
|
790
|
+
it('useState should update state', () => {
|
|
791
|
+
// Don't test React's functionality
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// ✅ Good - Test your code
|
|
795
|
+
it('should update count when button is clicked', () => {
|
|
796
|
+
// Test your component's behavior
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
**Don't use real external services**
|
|
801
|
+
```typescript
|
|
802
|
+
// ❌ Bad - Real API call
|
|
803
|
+
it('should fetch user', async () => {
|
|
804
|
+
const user = await fetch('https://api.example.com/users/1');
|
|
805
|
+
// Slow, unreliable, requires network
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// ✅ Good - Mocked API
|
|
809
|
+
it('should fetch user', async () => {
|
|
810
|
+
server.use(
|
|
811
|
+
http.get('/api/users/1', () => HttpResponse.json(mockUser))
|
|
812
|
+
);
|
|
813
|
+
const user = await fetchUser('1');
|
|
814
|
+
});
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
**Don't test too many things at once**
|
|
818
|
+
```typescript
|
|
819
|
+
// ❌ Bad - Testing multiple behaviors
|
|
820
|
+
it('should handle user lifecycle', async () => {
|
|
821
|
+
const user = await createUser({ name: 'John' });
|
|
822
|
+
await updateUser(user.id, { name: 'Jane' });
|
|
823
|
+
await deleteUser(user.id);
|
|
824
|
+
// Too much in one test
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
// ✅ Good - Separate tests
|
|
828
|
+
it('should create user', async () => { /* ... */ });
|
|
829
|
+
it('should update user', async () => { /* ... */ });
|
|
830
|
+
it('should delete user', async () => { /* ... */ });
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## Summary
|
|
836
|
+
|
|
837
|
+
**Key Takeaways:**
|
|
838
|
+
|
|
839
|
+
1. **Follow the testing pyramid** - Many unit tests, some integration tests, few E2E tests
|
|
840
|
+
2. **Use MSW for API mocking** - Intercept network requests at the network level
|
|
841
|
+
3. **Write descriptive test names** - Tests should document behavior
|
|
842
|
+
4. **Keep tests independent** - Each test should run in isolation
|
|
843
|
+
5. **Test behavior, not implementation** - Focus on what, not how
|
|
844
|
+
6. **Use property-based testing** - For testing invariants and edge cases
|
|
845
|
+
7. **Organize tests logically** - Co-locate tests with source code
|
|
846
|
+
8. **Use snapshots sparingly** - Prefer explicit assertions for critical logic
|
|
847
|
+
9. **Mock external dependencies** - Keep tests fast and reliable
|
|
848
|
+
10. **Practice TDD** - Write tests first, then implement
|
|
849
|
+
|
|
850
|
+
**Testing Checklist:**
|
|
851
|
+
|
|
852
|
+
- [ ] Unit tests for all business logic
|
|
853
|
+
- [ ] Integration tests for component interactions
|
|
854
|
+
- [ ] E2E tests for critical user flows
|
|
855
|
+
- [ ] API mocking with MSW
|
|
856
|
+
- [ ] Test coverage > 80%
|
|
857
|
+
- [ ] All tests pass in CI/CD
|
|
858
|
+
- [ ] Tests run in < 10 seconds (unit tests)
|
|
859
|
+
- [ ] No flaky tests
|
|
860
|
+
- [ ] Tests are maintainable and readable
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
## References
|
|
865
|
+
|
|
866
|
+
- [Vitest Documentation](https://vitest.dev/)
|
|
867
|
+
- [Testing Library](https://testing-library.com/)
|
|
868
|
+
- [MSW Documentation](https://mswjs.io/)
|
|
869
|
+
- [Playwright](https://playwright.dev/)
|
|
870
|
+
- [fast-check](https://fast-check.dev/)
|
|
871
|
+
- [Kent C. Dodds - Testing Best Practices](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
---
|
|
875
|
+
|
|
876
|
+
## Integration Testing
|
|
877
|
+
|
|
878
|
+
### Testing Component Integration
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
// user-profile.integration.test.tsx
|
|
882
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
883
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
884
|
+
import { UserProfile } from './user-profile';
|
|
885
|
+
import { setupServer } from 'msw/node';
|
|
886
|
+
import { http, HttpResponse } from 'msw';
|
|
887
|
+
|
|
888
|
+
const server = setupServer(
|
|
889
|
+
http.get('/api/users/:id', ({ params }) => {
|
|
890
|
+
return HttpResponse.json({
|
|
891
|
+
id: params.id,
|
|
892
|
+
name: 'John Doe',
|
|
893
|
+
email: 'john@example.com'
|
|
894
|
+
});
|
|
895
|
+
})
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
beforeAll(() => server.listen());
|
|
899
|
+
afterEach(() => server.resetHandlers());
|
|
900
|
+
afterAll(() => server.close());
|
|
901
|
+
|
|
902
|
+
describe('UserProfile Integration', () => {
|
|
903
|
+
it('should fetch and display user data', async () => {
|
|
904
|
+
render(<UserProfile userId="123" />);
|
|
905
|
+
|
|
906
|
+
// Initially shows loading
|
|
907
|
+
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
908
|
+
|
|
909
|
+
// Wait for data to load
|
|
910
|
+
await waitFor(() => {
|
|
911
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
expect(screen.getByText('john@example.com')).toBeInTheDocument();
|
|
915
|
+
});
|
|
916
|
+
});
|
|
917
|
+
```
|
|
918
|
+
|