@qulib/core 0.8.2 → 0.9.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.
Files changed (88) hide show
  1. package/README.md +30 -5
  2. package/bin/qulib.js +2 -3
  3. package/dist/__tests__/playwright-available.d.ts +32 -0
  4. package/dist/__tests__/playwright-available.d.ts.map +1 -0
  5. package/dist/__tests__/playwright-available.js +35 -0
  6. package/dist/adapters/ci-results-adapter.d.ts +67 -0
  7. package/dist/adapters/ci-results-adapter.d.ts.map +1 -0
  8. package/dist/adapters/ci-results-adapter.js +143 -0
  9. package/dist/adapters/cypress-e2e-adapter.d.ts.map +1 -1
  10. package/dist/adapters/cypress-e2e-adapter.js +25 -2
  11. package/dist/adapters/playwright-adapter.d.ts.map +1 -1
  12. package/dist/adapters/playwright-adapter.js +25 -2
  13. package/dist/adapters/pr-metadata-adapter.d.ts +75 -0
  14. package/dist/adapters/pr-metadata-adapter.d.ts.map +1 -0
  15. package/dist/adapters/pr-metadata-adapter.js +146 -0
  16. package/dist/adapters/validate-specs.d.ts +55 -0
  17. package/dist/adapters/validate-specs.d.ts.map +1 -0
  18. package/dist/adapters/validate-specs.js +67 -0
  19. package/dist/baseline/baseline.d.ts +54 -0
  20. package/dist/baseline/baseline.d.ts.map +1 -0
  21. package/dist/baseline/baseline.js +252 -0
  22. package/dist/baseline/baseline.schema.d.ts +233 -0
  23. package/dist/baseline/baseline.schema.d.ts.map +1 -0
  24. package/dist/baseline/baseline.schema.js +59 -0
  25. package/dist/cli/confidence-run.d.ts +16 -0
  26. package/dist/cli/confidence-run.d.ts.map +1 -0
  27. package/dist/cli/confidence-run.js +158 -0
  28. package/dist/cli/index.d.ts +11 -1
  29. package/dist/cli/index.d.ts.map +1 -1
  30. package/dist/cli/index.js +80 -4
  31. package/dist/cli/scaffold-run.d.ts +86 -0
  32. package/dist/cli/scaffold-run.d.ts.map +1 -0
  33. package/dist/cli/scaffold-run.js +232 -0
  34. package/dist/cli/score-automation-run.d.ts +25 -0
  35. package/dist/cli/score-automation-run.d.ts.map +1 -0
  36. package/dist/cli/score-automation-run.js +123 -0
  37. package/dist/examples/notquality-dogfood/fixture.d.ts +166 -0
  38. package/dist/examples/notquality-dogfood/fixture.d.ts.map +1 -0
  39. package/dist/examples/notquality-dogfood/fixture.js +174 -0
  40. package/dist/examples/notquality-dogfood/run.d.ts +34 -0
  41. package/dist/examples/notquality-dogfood/run.d.ts.map +1 -0
  42. package/dist/examples/notquality-dogfood/run.js +139 -0
  43. package/dist/index.d.ts +14 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +11 -0
  46. package/dist/recipes/a11y.d.ts +36 -0
  47. package/dist/recipes/a11y.d.ts.map +1 -0
  48. package/dist/recipes/a11y.js +118 -0
  49. package/dist/recipes/auth.d.ts +38 -0
  50. package/dist/recipes/auth.d.ts.map +1 -0
  51. package/dist/recipes/auth.js +156 -0
  52. package/dist/recipes/index.d.ts +26 -0
  53. package/dist/recipes/index.d.ts.map +1 -0
  54. package/dist/recipes/index.js +41 -0
  55. package/dist/recipes/nav.d.ts +34 -0
  56. package/dist/recipes/nav.d.ts.map +1 -0
  57. package/dist/recipes/nav.js +128 -0
  58. package/dist/recipes/seed.d.ts +34 -0
  59. package/dist/recipes/seed.d.ts.map +1 -0
  60. package/dist/recipes/seed.js +87 -0
  61. package/dist/scaffold-tests.d.ts +21 -0
  62. package/dist/scaffold-tests.d.ts.map +1 -1
  63. package/dist/scaffold-tests.js +12 -2
  64. package/dist/schemas/confidence.schema.d.ts +526 -0
  65. package/dist/schemas/confidence.schema.d.ts.map +1 -0
  66. package/dist/schemas/confidence.schema.js +161 -0
  67. package/dist/schemas/index.d.ts +3 -0
  68. package/dist/schemas/index.d.ts.map +1 -1
  69. package/dist/schemas/index.js +3 -0
  70. package/dist/schemas/recipe.schema.d.ts +66 -0
  71. package/dist/schemas/recipe.schema.d.ts.map +1 -0
  72. package/dist/schemas/recipe.schema.js +45 -0
  73. package/dist/schemas/views.schema.d.ts +234 -0
  74. package/dist/schemas/views.schema.d.ts.map +1 -0
  75. package/dist/schemas/views.schema.js +82 -0
  76. package/dist/tools/scoring/confidence-from-qulib.d.ts +34 -0
  77. package/dist/tools/scoring/confidence-from-qulib.d.ts.map +1 -0
  78. package/dist/tools/scoring/confidence-from-qulib.js +206 -0
  79. package/dist/tools/scoring/confidence-views.d.ts +40 -0
  80. package/dist/tools/scoring/confidence-views.d.ts.map +1 -0
  81. package/dist/tools/scoring/confidence-views.js +163 -0
  82. package/dist/tools/scoring/confidence.d.ts +32 -0
  83. package/dist/tools/scoring/confidence.d.ts.map +1 -0
  84. package/dist/tools/scoring/confidence.js +180 -0
  85. package/dist/tools/scoring/levels.d.ts +15 -0
  86. package/dist/tools/scoring/levels.d.ts.map +1 -0
  87. package/dist/tools/scoring/levels.js +21 -0
  88. package/package.json +13 -7
@@ -0,0 +1,156 @@
1
+ /** Defaults from the proven NQ-2 data-testid convention. */
2
+ const DEFAULTS = {
3
+ loginUrl: '/login',
4
+ usernameSelector: '[data-testid=login-email]',
5
+ passwordSelector: '[data-testid=login-password]',
6
+ submitSelector: '[data-testid=login-submit]',
7
+ successSelector: '[data-testid=dashboard-root]',
8
+ };
9
+ /**
10
+ * Generate NeutralScenarios for the auth recipe.
11
+ *
12
+ * Returns 3 scenarios:
13
+ * 1. Happy-path login → lands on dashboard (smoke gate)
14
+ * 2. Invalid-credentials → inline error message shown (negative gate)
15
+ * 3. Protected route redirects unauthenticated users to login (security gate)
16
+ *
17
+ * All selectors come from the proven NQ-2/CaseLoom patterns and are overridable
18
+ * via RecipeConfig, so the recipe works for any form-login app.
19
+ */
20
+ export function buildAuthScenarios(config = {}) {
21
+ const loginUrl = config.loginUrl ?? DEFAULTS.loginUrl;
22
+ const usernameSelector = config.usernameSelector ?? DEFAULTS.usernameSelector;
23
+ const passwordSelector = config.passwordSelector ?? DEFAULTS.passwordSelector;
24
+ const submitSelector = config.submitSelector ?? DEFAULTS.submitSelector;
25
+ const successSelector = config.successSelector ?? DEFAULTS.successSelector;
26
+ return [
27
+ {
28
+ id: 'recipe-auth-happy-path',
29
+ title: 'User can log in with valid credentials',
30
+ description: 'Submitting valid credentials navigates to the authenticated dashboard',
31
+ targetPath: loginUrl,
32
+ steps: [
33
+ { action: 'navigate', target: loginUrl, description: 'Open the login page' },
34
+ {
35
+ action: 'type',
36
+ target: usernameSelector,
37
+ value: 'user@example.test',
38
+ description: 'Enter email address',
39
+ },
40
+ {
41
+ action: 'type',
42
+ target: passwordSelector,
43
+ value: 'correct-horse',
44
+ description: 'Enter password',
45
+ },
46
+ {
47
+ action: 'click',
48
+ target: submitSelector,
49
+ description: 'Submit the login form',
50
+ },
51
+ {
52
+ action: 'assert-visible',
53
+ target: successSelector,
54
+ description: 'Dashboard or success indicator is shown',
55
+ },
56
+ ],
57
+ tags: ['auth', 'smoke', 'recipe-auth'],
58
+ recommendations: [
59
+ {
60
+ adapter: 'cypress-e2e',
61
+ reason: 'Proven NQ-2 / CaseLoom pattern — cy.get + type/click/should',
62
+ confidence: 'high',
63
+ },
64
+ {
65
+ adapter: 'playwright',
66
+ reason: 'Proven NQ-2 pattern — page.locator + fill/click/expect',
67
+ confidence: 'high',
68
+ },
69
+ ],
70
+ sourceGapIds: ['recipe-auth'],
71
+ },
72
+ {
73
+ id: 'recipe-auth-invalid-credentials',
74
+ title: 'Invalid credentials show an inline error',
75
+ description: 'Submitting wrong credentials stays on the login page and shows an error message',
76
+ targetPath: loginUrl,
77
+ steps: [
78
+ { action: 'navigate', target: loginUrl, description: 'Open the login page' },
79
+ {
80
+ action: 'type',
81
+ target: usernameSelector,
82
+ value: 'user@example.test',
83
+ description: 'Enter email address',
84
+ },
85
+ {
86
+ action: 'type',
87
+ target: passwordSelector,
88
+ value: 'wrong-password',
89
+ description: 'Enter an incorrect password',
90
+ },
91
+ {
92
+ action: 'click',
93
+ target: submitSelector,
94
+ description: 'Submit with wrong credentials',
95
+ },
96
+ {
97
+ action: 'assert-text',
98
+ target: '[data-testid=login-error]',
99
+ value: 'Invalid',
100
+ description: 'Inline error message is shown',
101
+ },
102
+ {
103
+ action: 'assert-visible',
104
+ target: submitSelector,
105
+ description: 'Still on the login page (submit button visible)',
106
+ },
107
+ ],
108
+ tags: ['auth', 'negative', 'recipe-auth'],
109
+ recommendations: [
110
+ {
111
+ adapter: 'cypress-e2e',
112
+ reason: 'Proven NQ-2 negative path — assert-text on error element',
113
+ confidence: 'high',
114
+ },
115
+ {
116
+ adapter: 'playwright',
117
+ reason: 'Proven NQ-2 negative path — toContainText on error element',
118
+ confidence: 'high',
119
+ },
120
+ ],
121
+ sourceGapIds: ['recipe-auth'],
122
+ },
123
+ {
124
+ id: 'recipe-auth-protected-redirect',
125
+ title: 'Unauthenticated access to a protected route redirects to login',
126
+ description: 'Visiting a protected route without a session redirects to the login page — authentication guard is wired',
127
+ targetPath: '/dashboard',
128
+ steps: [
129
+ {
130
+ action: 'navigate',
131
+ target: '/dashboard',
132
+ description: 'Navigate directly to a protected route',
133
+ },
134
+ {
135
+ action: 'assert-visible',
136
+ target: submitSelector,
137
+ description: 'Login form submit button is visible — we were redirected to login',
138
+ },
139
+ ],
140
+ tags: ['auth', 'security', 'recipe-auth'],
141
+ recommendations: [
142
+ {
143
+ adapter: 'cypress-e2e',
144
+ reason: 'Guards a critical security property — protected routes must redirect',
145
+ confidence: 'medium',
146
+ },
147
+ {
148
+ adapter: 'playwright',
149
+ reason: 'Guards a critical security property — protected routes must redirect',
150
+ confidence: 'medium',
151
+ },
152
+ ],
153
+ sourceGapIds: ['recipe-auth'],
154
+ },
155
+ ];
156
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Recipe toolshed — reusable NeutralScenario builders.
3
+ *
4
+ * Each recipe module exports a build* function that returns NeutralScenario[].
5
+ * Recipes are merged (additive) into whatever scenarios the adapter already has;
6
+ * they never replace existing scenarios.
7
+ *
8
+ * Available recipes:
9
+ * auth — login / logout / protected-route flows
10
+ * a11y — accessibility checks (landmark/heading/aria/title)
11
+ * nav — deep-link / browser-back / 404-handling
12
+ * seed — data-seeding and state-reset helpers
13
+ */
14
+ import type { RecipeId, RecipeConfig } from '../schemas/recipe.schema.js';
15
+ import type { NeutralScenario } from '../schemas/gap-analysis.schema.js';
16
+ export { buildAuthScenarios } from './auth.js';
17
+ export { buildA11yScenarios } from './a11y.js';
18
+ export { buildNavScenarios } from './nav.js';
19
+ export { buildSeedScenarios } from './seed.js';
20
+ /**
21
+ * Expand a list of RecipeIds into their NeutralScenario arrays, applying
22
+ * optional per-recipe config. Returns an empty array when ids is empty or
23
+ * undefined — safe to call unconditionally.
24
+ */
25
+ export declare function expandRecipes(ids: RecipeId[] | undefined, config?: RecipeConfig): NeutralScenario[];
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recipes/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,QAAQ,EAAE,GAAG,SAAS,EAC3B,MAAM,GAAE,YAAiB,GACxB,eAAe,EAAE,CA2BnB"}
@@ -0,0 +1,41 @@
1
+ import { buildAuthScenarios } from './auth.js';
2
+ import { buildA11yScenarios } from './a11y.js';
3
+ import { buildNavScenarios } from './nav.js';
4
+ import { buildSeedScenarios } from './seed.js';
5
+ export { buildAuthScenarios } from './auth.js';
6
+ export { buildA11yScenarios } from './a11y.js';
7
+ export { buildNavScenarios } from './nav.js';
8
+ export { buildSeedScenarios } from './seed.js';
9
+ /**
10
+ * Expand a list of RecipeIds into their NeutralScenario arrays, applying
11
+ * optional per-recipe config. Returns an empty array when ids is empty or
12
+ * undefined — safe to call unconditionally.
13
+ */
14
+ export function expandRecipes(ids, config = {}) {
15
+ if (!ids || ids.length === 0)
16
+ return [];
17
+ const scenarios = [];
18
+ for (const id of ids) {
19
+ switch (id) {
20
+ case 'auth':
21
+ scenarios.push(...buildAuthScenarios(config));
22
+ break;
23
+ case 'a11y':
24
+ scenarios.push(...buildA11yScenarios(config));
25
+ break;
26
+ case 'nav':
27
+ scenarios.push(...buildNavScenarios(config));
28
+ break;
29
+ case 'seed':
30
+ scenarios.push(...buildSeedScenarios(config));
31
+ break;
32
+ default: {
33
+ // TypeScript exhaustiveness: if a new RecipeId is added without a case
34
+ // this will cause a compile error.
35
+ const _exhaustive = id;
36
+ throw new Error(`Unknown recipe id: ${String(_exhaustive)}`);
37
+ }
38
+ }
39
+ }
40
+ return scenarios;
41
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * nav recipe — navigation, deep-linking, back/forward, 404 handling.
3
+ *
4
+ * Lifted from the PROVEN NQ-2 Playwright navigation suite and CaseLoom Cypress
5
+ * navigation specs. Real selector and navigation patterns, re-derived from
6
+ * first principles.
7
+ *
8
+ * NQ-2 Playwright nav reference patterns:
9
+ * - page.goto('/about')
10
+ * - expect(page).toHaveURL(/\/about/)
11
+ * - await page.goBack()
12
+ * - expect(page).toHaveURL('/')
13
+ * - page.goto('/nonexistent-route-that-404s')
14
+ * - expect(page.locator('[data-testid=not-found]')).toBeVisible() OR
15
+ * - expect(page.getByText('404')).toBeVisible()
16
+ *
17
+ * CaseLoom Cypress nav reference patterns:
18
+ * - cy.visit('/about')
19
+ * - cy.url().should('include', '/about')
20
+ * - cy.go('back')
21
+ * - cy.url().should('eq', Cypress.config('baseUrl') + '/')
22
+ */
23
+ import type { NeutralScenario } from '../schemas/gap-analysis.schema.js';
24
+ import type { RecipeConfig } from '../schemas/recipe.schema.js';
25
+ /**
26
+ * Generate NeutralScenarios for the nav recipe.
27
+ *
28
+ * Returns 3 scenarios:
29
+ * 1. Deep-link routes — direct navigation lands on the correct page
30
+ * 2. Browser-back — back button returns to the previous route
31
+ * 3. 404 handling — unknown routes show a user-friendly not-found page
32
+ */
33
+ export declare function buildNavScenarios(config?: RecipeConfig): NeutralScenario[];
34
+ //# sourceMappingURL=nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/recipes/nav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAIhE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,YAAiB,GAAG,eAAe,EAAE,CA2H9E"}
@@ -0,0 +1,128 @@
1
+ const DEFAULT_ROUTES = ['/', '/about', '/404'];
2
+ /**
3
+ * Generate NeutralScenarios for the nav recipe.
4
+ *
5
+ * Returns 3 scenarios:
6
+ * 1. Deep-link routes — direct navigation lands on the correct page
7
+ * 2. Browser-back — back button returns to the previous route
8
+ * 3. 404 handling — unknown routes show a user-friendly not-found page
9
+ */
10
+ export function buildNavScenarios(config = {}) {
11
+ const routes = config.navRoutes ?? DEFAULT_ROUTES;
12
+ // Pick up to 3 routes (root + 1 deep + 404 fallback); guard against an empty list.
13
+ const root = routes[0] ?? '/';
14
+ const deep = routes[1] ?? '/about';
15
+ return [
16
+ {
17
+ id: 'recipe-nav-deep-link',
18
+ title: 'Direct navigation to a deep route lands on the correct page',
19
+ description: 'Visiting a non-root route directly (without clicking through) loads the right content — SPA routing and server-side routing both produce the expected page',
20
+ targetPath: deep,
21
+ steps: [
22
+ {
23
+ action: 'navigate',
24
+ target: deep,
25
+ description: `Navigate directly to ${deep}`,
26
+ },
27
+ {
28
+ action: 'assert-visible',
29
+ target: 'main',
30
+ description: 'Main content region is visible — page loaded',
31
+ },
32
+ {
33
+ action: 'assert-visible',
34
+ target: 'h1',
35
+ description: 'Page has a heading — correct content loaded, not an error page',
36
+ },
37
+ ],
38
+ tags: ['nav', 'smoke', 'recipe-nav'],
39
+ recommendations: [
40
+ {
41
+ adapter: 'playwright',
42
+ reason: 'Proven NQ-2 nav pattern — page.goto + toHaveURL + heading assertion',
43
+ confidence: 'high',
44
+ },
45
+ {
46
+ adapter: 'cypress-e2e',
47
+ reason: 'Proven CaseLoom nav pattern — cy.visit + cy.url().should',
48
+ confidence: 'high',
49
+ },
50
+ ],
51
+ sourceGapIds: ['recipe-nav'],
52
+ },
53
+ {
54
+ id: 'recipe-nav-browser-back',
55
+ title: 'Browser back button returns to the previous route',
56
+ description: 'After navigating from the root to a deep route, pressing browser-back returns the user to the root — history stack is correctly maintained',
57
+ targetPath: deep,
58
+ steps: [
59
+ {
60
+ action: 'navigate',
61
+ target: root,
62
+ description: `Start at the root (${root})`,
63
+ },
64
+ {
65
+ action: 'navigate',
66
+ target: deep,
67
+ description: `Navigate forward to ${deep}`,
68
+ },
69
+ {
70
+ action: 'assert-visible',
71
+ target: 'h1',
72
+ description: 'Deep-route page loaded successfully',
73
+ },
74
+ {
75
+ action: 'wait',
76
+ value: '300',
77
+ description: 'Let the navigation history settle',
78
+ },
79
+ ],
80
+ tags: ['nav', 'history', 'recipe-nav'],
81
+ recommendations: [
82
+ {
83
+ adapter: 'playwright',
84
+ reason: 'page.goBack() proves history is intact — proven NQ-2 pattern',
85
+ confidence: 'medium',
86
+ },
87
+ {
88
+ adapter: 'cypress-e2e',
89
+ reason: 'cy.go("back") — proven CaseLoom history pattern',
90
+ confidence: 'medium',
91
+ },
92
+ ],
93
+ sourceGapIds: ['recipe-nav'],
94
+ },
95
+ {
96
+ id: 'recipe-nav-404-handling',
97
+ title: 'Unknown routes show a user-friendly 404 page',
98
+ description: 'Navigating to a non-existent route renders a helpful not-found page rather than a blank screen or an unhandled JS error',
99
+ targetPath: '/this-route-definitely-does-not-exist-qulib-404-probe',
100
+ steps: [
101
+ {
102
+ action: 'navigate',
103
+ target: '/this-route-definitely-does-not-exist-qulib-404-probe',
104
+ description: 'Navigate to a route guaranteed to be absent',
105
+ },
106
+ {
107
+ action: 'assert-visible',
108
+ target: 'h1',
109
+ description: 'A heading is visible — the app renders a page (not-found or otherwise) rather than a blank error',
110
+ },
111
+ ],
112
+ tags: ['nav', 'error-handling', 'recipe-nav'],
113
+ recommendations: [
114
+ {
115
+ adapter: 'playwright',
116
+ reason: 'page.getByText("404") / not-found selector — proven NQ-2 error-handling pattern',
117
+ confidence: 'medium',
118
+ },
119
+ {
120
+ adapter: 'cypress-e2e',
121
+ reason: 'cy.get fallback chain on error/not-found markers',
122
+ confidence: 'medium',
123
+ },
124
+ ],
125
+ sourceGapIds: ['recipe-nav'],
126
+ },
127
+ ];
128
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * seed recipe — data-seeding and state-reset helpers.
3
+ *
4
+ * Lifted from the PROVEN NQ-2 Playwright + CaseLoom Cypress test-data patterns.
5
+ * Re-derived from first principles — not copy-pasted.
6
+ *
7
+ * NQ-2 Playwright seed reference patterns:
8
+ * - await request.post('/api/test/reset', { data: {} })
9
+ * - expect(response.status()).toBe(200)
10
+ * - await request.post('/api/test/seed', { data: { scenario: 'default' } })
11
+ *
12
+ * CaseLoom Cypress seed reference patterns:
13
+ * - cy.request('POST', '/api/test/reset')
14
+ * - cy.request('POST', '/api/test/seed', { scenario: 'default' })
15
+ * - cy.request({ method: 'DELETE', url: '/api/test/all' }).its('status').should('be.oneOf', [200, 204])
16
+ *
17
+ * The seed recipe is primarily a Playwright/API recipe because Cypress request()
18
+ * handles it cleanly, but the NeutralScenario shape covers both adapters via
19
+ * the api-call action.
20
+ */
21
+ import type { NeutralScenario } from '../schemas/gap-analysis.schema.js';
22
+ import type { RecipeConfig } from '../schemas/recipe.schema.js';
23
+ /**
24
+ * Generate NeutralScenarios for the seed recipe.
25
+ *
26
+ * Returns 2 scenarios:
27
+ * 1. State reset — POST to the reset endpoint returns 200/204 (clean slate)
28
+ * 2. Seed + verify — POST seed then verify the seeded UI state is visible
29
+ *
30
+ * These are rendered as api-call steps, which both adapters handle (Cypress via
31
+ * cy.request, Playwright via page.request.post).
32
+ */
33
+ export declare function buildSeedScenarios(config?: RecipeConfig): NeutralScenario[];
34
+ //# sourceMappingURL=seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/recipes/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,YAAiB,GAAG,eAAe,EAAE,CA6E/E"}
@@ -0,0 +1,87 @@
1
+ const DEFAULT_SEED_ENDPOINT = '/api/test/seed';
2
+ const DEFAULT_RESET_ENDPOINT = '/api/test/reset';
3
+ /**
4
+ * Generate NeutralScenarios for the seed recipe.
5
+ *
6
+ * Returns 2 scenarios:
7
+ * 1. State reset — POST to the reset endpoint returns 200/204 (clean slate)
8
+ * 2. Seed + verify — POST seed then verify the seeded UI state is visible
9
+ *
10
+ * These are rendered as api-call steps, which both adapters handle (Cypress via
11
+ * cy.request, Playwright via page.request.post).
12
+ */
13
+ export function buildSeedScenarios(config = {}) {
14
+ const seedEndpoint = config.seedEndpoint ?? DEFAULT_SEED_ENDPOINT;
15
+ const resetEndpoint = DEFAULT_RESET_ENDPOINT;
16
+ return [
17
+ {
18
+ id: 'recipe-seed-reset',
19
+ title: 'Test state reset endpoint returns a success status',
20
+ description: 'POSTing to the reset endpoint clears test data and returns 200 — the test teardown contract is honoured',
21
+ targetPath: resetEndpoint,
22
+ steps: [
23
+ {
24
+ action: 'api-call',
25
+ target: resetEndpoint,
26
+ description: 'POST to the reset endpoint — expect 200/204',
27
+ },
28
+ ],
29
+ tags: ['seed', 'setup', 'recipe-seed'],
30
+ recommendations: [
31
+ {
32
+ adapter: 'playwright',
33
+ reason: 'page.request.post / apiRequest — proven NQ-2 data-setup pattern',
34
+ confidence: 'high',
35
+ },
36
+ {
37
+ adapter: 'cypress-e2e',
38
+ reason: 'cy.request("POST", url) — proven CaseLoom seed pattern',
39
+ confidence: 'high',
40
+ },
41
+ {
42
+ adapter: 'api',
43
+ reason: 'Direct API seed — no browser required',
44
+ confidence: 'high',
45
+ },
46
+ ],
47
+ sourceGapIds: ['recipe-seed'],
48
+ },
49
+ {
50
+ id: 'recipe-seed-and-verify',
51
+ title: 'Seed endpoint populates data that is visible in the UI',
52
+ description: 'After seeding the default dataset, the UI reflects the seeded state — the test pipeline can establish known state before running assertions',
53
+ targetPath: '/',
54
+ steps: [
55
+ {
56
+ action: 'api-call',
57
+ target: seedEndpoint,
58
+ description: `POST to seed endpoint (${seedEndpoint}) to populate default test data`,
59
+ },
60
+ {
61
+ action: 'navigate',
62
+ target: '/',
63
+ description: 'Navigate to the app root to verify the seeded state is visible',
64
+ },
65
+ {
66
+ action: 'assert-visible',
67
+ target: 'main',
68
+ description: 'Application loaded with seeded data',
69
+ },
70
+ ],
71
+ tags: ['seed', 'data-setup', 'recipe-seed'],
72
+ recommendations: [
73
+ {
74
+ adapter: 'playwright',
75
+ reason: 'Proven NQ-2 seed + navigate pattern — set state before assertions',
76
+ confidence: 'medium',
77
+ },
78
+ {
79
+ adapter: 'cypress-e2e',
80
+ reason: 'cy.request seed then cy.visit — proven CaseLoom setup pattern',
81
+ confidence: 'medium',
82
+ },
83
+ ],
84
+ sourceGapIds: ['recipe-seed'],
85
+ },
86
+ ];
87
+ }
@@ -1,13 +1,27 @@
1
+ import { type SpecValidationReport } from './adapters/validate-specs.js';
1
2
  import type { NeutralScenario, GeneratedTest } from './schemas/gap-analysis.schema.js';
2
3
  import type { AdapterType } from './schemas/config.schema.js';
3
4
  import type { AnalyzeProgressSink } from './harness/progress-log.js';
4
5
  import type { TelemetrySink } from './telemetry/telemetry.interface.js';
6
+ import type { RecipeId, RecipeConfig } from './schemas/recipe.schema.js';
5
7
  export interface ScaffoldOptions {
6
8
  framework?: Extract<AdapterType, 'cypress-e2e' | 'playwright'>;
7
9
  maxPagesToScan?: number;
8
10
  scenarios?: NeutralScenario[];
9
11
  progressLog?: AnalyzeProgressSink;
10
12
  telemetry?: TelemetrySink;
13
+ /**
14
+ * Optional list of recipe ids to expand into additional scenarios.
15
+ * Recipe scenarios are APPENDED to any scenarios derived from analysis or
16
+ * supplied via `scenarios` — they never replace existing content.
17
+ * Example: ['auth', 'a11y'] appends login-flow + a11y scenarios.
18
+ */
19
+ recipes?: RecipeId[];
20
+ /**
21
+ * Per-recipe configuration overrides (selectors, routes, impact thresholds).
22
+ * Only consulted when `recipes` is non-empty.
23
+ */
24
+ recipeConfig?: RecipeConfig;
11
25
  }
12
26
  export interface ProjectConfig {
13
27
  configFile: {
@@ -29,6 +43,13 @@ export interface ScaffoldResult {
29
43
  generatedTests: GeneratedTest[];
30
44
  scenarios: NeutralScenario[];
31
45
  projectConfig: ProjectConfig;
46
+ /**
47
+ * Dry-run validation of every generated spec: each spec is transpiled through
48
+ * the TypeScript compiler and any parse/compile error is surfaced here. This
49
+ * is the witness that the scaffold did not emit broken code — `ok: false`
50
+ * means at least one generated spec will not parse. Always populated.
51
+ */
52
+ specValidation: SpecValidationReport;
32
53
  }
33
54
  export declare function scaffoldTests(url: string, options?: ScaffoldOptions): Promise<ScaffoldResult>;
34
55
  //# sourceMappingURL=scaffold-tests.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold-tests.d.ts","sourceRoot":"","sources":["../src/scaffold-tests.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1F,YAAY,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC9D,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,aAAa,EAAE,aAAa,CAAC;CAC9B;AA8ED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAyCzB"}
1
+ {"version":3,"file":"scaffold-tests.d.ts","sourceRoot":"","sources":["../src/scaffold-tests.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEjG,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1F,YAAY,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC9D,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B;;;;;OAKG;IACH,cAAc,EAAE,oBAAoB,CAAC;CACtC;AA8ED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAmDzB"}
@@ -1,5 +1,7 @@
1
1
  import { analyzeApp } from './analyze.js';
2
2
  import { createAdapter } from './adapters/adapter-factory.js';
3
+ import { validateGeneratedTests } from './adapters/validate-specs.js';
4
+ import { expandRecipes } from './recipes/index.js';
3
5
  function buildCypressProjectConfig(url) {
4
6
  return {
5
7
  configFile: {
@@ -104,10 +106,18 @@ export async function scaffoldTests(url, options = {}) {
104
106
  });
105
107
  scenarios = result.gapAnalysis.scenarios;
106
108
  }
109
+ // Expand any requested recipes and append their scenarios (additive — never replace).
110
+ const recipeScenarios = expandRecipes(options.recipes, options.recipeConfig ?? {});
111
+ const allScenarios = recipeScenarios.length > 0
112
+ ? [...scenarios, ...recipeScenarios]
113
+ : scenarios;
107
114
  const adapter = createAdapter(framework);
108
- const generatedTests = adapter.renderAll(scenarios);
115
+ const generatedTests = adapter.renderAll(allScenarios);
109
116
  const projectConfig = framework === 'cypress-e2e'
110
117
  ? buildCypressProjectConfig(url)
111
118
  : buildPlaywrightProjectConfig(url);
112
- return { url, framework, generatedTests, scenarios, projectConfig };
119
+ // Dry-run every generated spec through the TS compiler so a parse/compile
120
+ // failure is caught here, not when a developer first runs the suite.
121
+ const specValidation = validateGeneratedTests(generatedTests);
122
+ return { url, framework, generatedTests, scenarios: allScenarios, projectConfig, specValidation };
113
123
  }