test-casebook 1.0.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.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
3
+ import { dirname, join, relative } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ const pkgRoot = dirname(here);
8
+ const cwd = process.cwd();
9
+ const args = process.argv.slice(2);
10
+ const command = args[0];
11
+ const force = args.includes("--force");
12
+
13
+ const ASSETS = ["AGENTS.md", "docs", ".claude"];
14
+
15
+ function walk(dir) {
16
+ const out = [];
17
+ for (const entry of readdirSync(dir)) {
18
+ const full = join(dir, entry);
19
+ if (statSync(full).isDirectory()) out.push(...walk(full));
20
+ else out.push(full);
21
+ }
22
+ return out;
23
+ }
24
+
25
+ function copyAsset(name) {
26
+ const src = join(pkgRoot, name);
27
+ if (!existsSync(src)) return { copied: [], skipped: [] };
28
+ const copied = [];
29
+ const skipped = [];
30
+ const files = statSync(src).isDirectory() ? walk(src) : [src];
31
+ for (const file of files) {
32
+ const rel = relative(pkgRoot, file);
33
+ const dest = join(cwd, rel);
34
+ if (existsSync(dest) && !force) {
35
+ skipped.push(rel);
36
+ continue;
37
+ }
38
+ mkdirSync(dirname(dest), { recursive: true });
39
+ cpSync(file, dest);
40
+ copied.push(rel);
41
+ }
42
+ return { copied, skipped };
43
+ }
44
+
45
+ function init() {
46
+ const copied = [];
47
+ const skipped = [];
48
+ for (const asset of ASSETS) {
49
+ const result = copyAsset(asset);
50
+ copied.push(...result.copied);
51
+ skipped.push(...result.skipped);
52
+ }
53
+ console.log(`\ntest-casebook — scaffolded into ${cwd}\n`);
54
+ if (copied.length) {
55
+ console.log(`Added ${copied.length} file(s):`);
56
+ for (const file of copied) console.log(` + ${file}`);
57
+ }
58
+ if (skipped.length) {
59
+ console.log(
60
+ `\nSkipped ${skipped.length} existing file(s) (use --force to overwrite):`,
61
+ );
62
+ for (const file of skipped) console.log(` = ${file}`);
63
+ }
64
+ console.log(`\nNext steps:`);
65
+ console.log(
66
+ ` 1. Open this project in Claude Code and invoke the "test-casebook" skill.`,
67
+ );
68
+ console.log(
69
+ ` 2. env-attr-cleaner (the cleaner) is OPTIONAL and never auto-installed.`,
70
+ );
71
+ console.log(
72
+ ` To strip data-test-* from production builds, install it yourself:`,
73
+ );
74
+ console.log(
75
+ ` npm i -D env-attr-cleaner (or pnpm add -D / yarn add -D)`,
76
+ );
77
+ console.log(
78
+ ` then wire it per the env-attr-cleaner repo's docs/frameworks.\n`,
79
+ );
80
+ }
81
+
82
+ function usage() {
83
+ console.log(`test-casebook — testing methodology scaffolder\n`);
84
+ console.log(`Usage:`);
85
+ console.log(` npx test-casebook init [--force]\n`);
86
+ console.log(
87
+ ` init copy AGENTS.md, docs/ and .claude/ into the current project`,
88
+ );
89
+ console.log(` --force overwrite files that already exist\n`);
90
+ }
91
+
92
+ if (command === "init") init();
93
+ else usage();
@@ -0,0 +1,127 @@
1
+
2
+ # Conventions - Data Attributes
3
+
4
+ ## Test Attributes
5
+
6
+ ### `data-test-id`
7
+
8
+ Unique identifier for a specific element.
9
+
10
+ ```html
11
+ <button data-test-id="login-submit">Connexion</button>
12
+ <input data-test-id="login-email" type="email">
13
+ <form data-test-id="login-form">
14
+ ```
15
+
16
+ **Naming**: `context-element` or `context-action`
17
+
18
+ ```
19
+ login-submit
20
+ login-email
21
+ cart-checkout-button
22
+ product-123-add-to-cart
23
+ ```
24
+
25
+ ### `data-test-class`
26
+
27
+ Group similar elements for counting or filtering.
28
+
29
+ ```html
30
+ <div data-test-class="product-card">...</div>
31
+ <div data-test-class="product-card">...</div>
32
+ <div data-test-class="product-card featured">...</div>
33
+ ```
34
+
35
+ **Usage in tests**:
36
+
37
+ ```ts
38
+ // Compter
39
+ const cards = page.locator('[data-test-class~="product-card"]')
40
+ await expect(cards).toHaveCount(3)
41
+
42
+ // Filtrer
43
+ const featured = page.locator('[data-test-class~="featured"]')
44
+ ```
45
+
46
+ ## Debug Attributes (Phase 2)
47
+
48
+ ### `data-state`
49
+
50
+ Current business state of a component.
51
+
52
+ ```html
53
+ <form data-state="idle">
54
+ <form data-state="loading">
55
+ <form data-state="success">
56
+ <form data-state="error">
57
+ ```
58
+
59
+ ### `data-error`
60
+
61
+ Error context for debugging.
62
+
63
+ ```html
64
+ <div data-error="validation-failed">
65
+ <div data-error="network-timeout">
66
+ ```
67
+
68
+ ### `data-last-action`
69
+
70
+ Last user action for debugging.
71
+
72
+ ```html
73
+ <div data-last-action="submit-clicked">
74
+ <div data-last-action="field-focused">
75
+ ```
76
+
77
+ ## Feature Flags (Phase 3)
78
+
79
+ ### `data-present`
80
+
81
+ Track if an element should be present.
82
+
83
+ ```html
84
+ <div data-present="premium-feature">
85
+ ```
86
+
87
+ ### `data-experiment` / `data-variant`
88
+
89
+ A/B test tracking.
90
+
91
+ ```html
92
+ <button data-experiment="checkout-flow" data-variant="one-click">
93
+ ```
94
+
95
+ ## Analytics (Phase 4)
96
+
97
+ ### `data-analytics-*`
98
+
99
+ Analytics event tracking.
100
+
101
+ ```html
102
+ <button
103
+ data-analytics-event="click"
104
+ data-analytics-category="checkout"
105
+ data-analytics-label="buy-now"
106
+ >
107
+ ```
108
+
109
+ ## Configuration by Environment
110
+
111
+ ```ts
112
+ envAttrCleaner({
113
+ environments: {
114
+ // Tous les attributs en developpement
115
+ development: ['data-test-*', 'data-debug-*', 'data-analytics-*'],
116
+
117
+ // Uniquement les attributs de test en CI
118
+ test: ['data-test-*'],
119
+
120
+ // Test + analytics en staging
121
+ staging: ['data-test-*', 'data-analytics-*'],
122
+
123
+ // Uniquement analytics en production (ou rien)
124
+ production: ['data-analytics-*']
125
+ }
126
+ })
127
+ ```
@@ -0,0 +1,100 @@
1
+
2
+ # Strategy - Why Data Attributes for Testing
3
+
4
+ ## The Problem
5
+
6
+ Traditional selectors are **fragile**:
7
+
8
+ ```ts
9
+ // Breaks when CSS changes
10
+ cy.get('.btn-primary')
11
+
12
+ // Breaks when structure changes
13
+ cy.get('form > div:nth-child(2) > button')
14
+
15
+ // Breaks when text changes
16
+ cy.get('button').contains('Envoyer')
17
+ ```
18
+
19
+ ## The Solution
20
+
21
+ Data attributes are **stable**:
22
+
23
+ ```ts
24
+ // Never breaks unless intentionally changed
25
+ cy.get('[data-test-id="submit-button"]')
26
+ ```
27
+
28
+ ## Why It Works
29
+
30
+ | Selector type | Stability | Readability |
31
+ |---------------|-----------|-------------|
32
+ | CSS classes | Low | Medium |
33
+ | DOM structure | Very low | Low |
34
+ | Text content | Low | High |
35
+ | `data-test-*` | **High** | **High** |
36
+
37
+ ## Conventions
38
+
39
+ ### `data-test-id` - Target a unique element
40
+
41
+ ```html
42
+ <button data-test-id="login-submit">Connexion</button>
43
+ ```
44
+
45
+ ```ts
46
+ page.locator('[data-test-id="login-submit"]')
47
+ ```
48
+
49
+ ### `data-test-class` - Group elements
50
+
51
+ ```html
52
+ <div data-test-class="product-card">Produit 1</div>
53
+ <div data-test-class="product-card">Produit 2</div>
54
+ <div data-test-class="product-card">Produit 3</div>
55
+ ```
56
+
57
+ ```ts
58
+ const cards = page.locator('[data-test-class="product-card"]')
59
+ await expect(cards).toHaveCount(3)
60
+ ```
61
+
62
+ ## Adoption Roadmap
63
+
64
+ ### Phase 1 - Testing (immediate value)
65
+
66
+ ```
67
+ data-test-id -> unique element targeting
68
+ data-test-class -> group counting and filtering
69
+ ```
70
+
71
+ ### Phase 2 - Debug (developer productivity)
72
+
73
+ ```
74
+ data-state -> visible business state
75
+ data-error -> error context
76
+ data-last-action -> last user action
77
+ ```
78
+
79
+ ### Phase 3 - Feature Flags / A/B Testing
80
+
81
+ ```
82
+ data-present -> element presence tracking
83
+ data-experiment -> A/B test name
84
+ data-variant -> active variant
85
+ ```
86
+
87
+ ### Phase 4 - Analytics
88
+
89
+ ```
90
+ data-analytics-event -> event name
91
+ data-analytics-category -> category
92
+ data-analytics-label -> custom label
93
+ ```
94
+
95
+ ## Key Message
96
+
97
+ > Start by testing better.
98
+ > End up understanding and controlling your application.
99
+
100
+ Data attributes become a **common language** for the entire frontend: testing, debug, observability, analytics.