@qulib/core 0.6.0 → 0.7.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cypress-e2e-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/cypress-e2e-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAExF,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,WAAW,iBAAiB;IAErC,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,aAAa;IAIhD,SAAS,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE;CAGzD"}
1
+ {"version":3,"file":"cypress-e2e-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/cypress-e2e-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAY,MAAM,mCAAmC,CAAC;AA2ClG,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,WAAW,iBAAiB;IAErC,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,aAAa;IA4BhD,SAAS,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE;CAGzD"}
@@ -1,9 +1,70 @@
1
+ function slugify(title) {
2
+ return title
3
+ .toLowerCase()
4
+ .replace(/[^a-z0-9]+/g, '-')
5
+ .replace(/^-|-$/g, '');
6
+ }
7
+ function renderStep(step) {
8
+ const t = step.target != null ? JSON.stringify(step.target) : null;
9
+ const v = step.value != null ? JSON.stringify(step.value) : null;
10
+ switch (step.action) {
11
+ case 'navigate':
12
+ return ` cy.visit(${JSON.stringify(step.target ?? step.value ?? '/')});`;
13
+ case 'click':
14
+ return t ? ` cy.get(${t}).click();` : ` // click: ${step.description}`;
15
+ case 'type':
16
+ return t && v ? ` cy.get(${t}).type(${v});` : ` // type: ${step.description}`;
17
+ case 'assert-visible':
18
+ return t ? ` cy.get(${t}).should('be.visible');` : ` cy.get('body').should('be.visible');`;
19
+ case 'assert-hidden':
20
+ return t ? ` cy.get(${t}).should('not.be.visible');` : ` // assert-hidden: ${step.description}`;
21
+ case 'assert-text':
22
+ if (t && v)
23
+ return ` cy.get(${t}).should('contain.text', ${v});`;
24
+ if (t)
25
+ return ` cy.get(${t}).should('not.be.empty');`;
26
+ return ` // assert-text: ${step.description}`;
27
+ case 'assert-disabled':
28
+ return t ? ` cy.get(${t}).should('be.disabled');` : ` // assert-disabled: ${step.description}`;
29
+ case 'assert-count':
30
+ return t
31
+ ? ` cy.get(${t}).should('have.length.gte', ${parseInt(step.value ?? '1', 10)});`
32
+ : ` // assert-count: ${step.description}`;
33
+ case 'wait':
34
+ return ` cy.wait(${parseInt(step.value ?? '1000', 10)});`;
35
+ case 'api-call':
36
+ return ` cy.request(${JSON.stringify(step.target ?? step.value ?? '/')}).its('status').should('eq', 200);`;
37
+ default:
38
+ return ` // TODO: ${step.description}`;
39
+ }
40
+ }
1
41
  export class CypressE2EAdapter {
2
42
  adapterType = 'cypress-e2e';
3
43
  render(scenario) {
4
- throw new Error('Not implemented');
44
+ const slug = slugify(scenario.title);
45
+ const filename = `${slug}.cy.ts`;
46
+ const stepLines = scenario.steps.map(renderStep).join('\n');
47
+ const code = [
48
+ `// ${scenario.description}`,
49
+ `// qulib-generated — scenario: ${scenario.id}`,
50
+ ``,
51
+ `describe(${JSON.stringify(scenario.title)}, () => {`,
52
+ ` it(${JSON.stringify(scenario.description)}, () => {`,
53
+ stepLines || ` // no steps — add assertions for: ${scenario.targetPath}`,
54
+ ` });`,
55
+ `});`,
56
+ ``,
57
+ ].join('\n');
58
+ return {
59
+ scenarioId: scenario.id,
60
+ adapter: 'cypress-e2e',
61
+ filename,
62
+ code,
63
+ source: 'template',
64
+ outputPath: `cypress/e2e/${filename}`,
65
+ };
5
66
  }
6
67
  renderAll(scenarios) {
7
- throw new Error('Not implemented');
68
+ return scenarios.map((s) => this.render(s));
8
69
  }
9
70
  }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export { exploreAuth } from './tools/auth/explore.js';
7
7
  export { addUserProvider, removeUserProvider, listUserProviders } from './tools/auth/custom-providers.js';
8
8
  export { scanRepo } from './tools/repo/scan.js';
9
9
  export { computeAutomationMaturity } from './tools/scoring/automation-maturity.js';
10
+ export { scaffoldTests } from './scaffold-tests.js';
11
+ export type { ScaffoldOptions, ScaffoldResult, ProjectConfig } from './scaffold-tests.js';
10
12
  export { createProvider } from './llm/provider-registry.js';
11
13
  export { resolveMaxOutputTokensPerLlmCall } from './schemas/config.schema.js';
12
14
  export { resolveScanStateBaseDir, resolveReportDir } from './harness/state-manager.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,4BAA4B,EAC5B,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1G,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACvF,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EACV,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,4BAA4B,EAC5B,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1G,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACvF,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EACV,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { exploreAuth } from './tools/auth/explore.js';
5
5
  export { addUserProvider, removeUserProvider, listUserProviders } from './tools/auth/custom-providers.js';
6
6
  export { scanRepo } from './tools/repo/scan.js';
7
7
  export { computeAutomationMaturity } from './tools/scoring/automation-maturity.js';
8
+ export { scaffoldTests } from './scaffold-tests.js';
8
9
  export { createProvider } from './llm/provider-registry.js';
9
10
  export { resolveMaxOutputTokensPerLlmCall } from './schemas/config.schema.js';
10
11
  export { resolveScanStateBaseDir, resolveReportDir } from './harness/state-manager.js';
@@ -0,0 +1,34 @@
1
+ import type { NeutralScenario, GeneratedTest } from './schemas/gap-analysis.schema.js';
2
+ import type { AdapterType } from './schemas/config.schema.js';
3
+ import type { AnalyzeProgressSink } from './harness/progress-log.js';
4
+ import type { TelemetrySink } from './telemetry/telemetry.interface.js';
5
+ export interface ScaffoldOptions {
6
+ framework?: Extract<AdapterType, 'cypress-e2e' | 'playwright'>;
7
+ maxPagesToScan?: number;
8
+ scenarios?: NeutralScenario[];
9
+ progressLog?: AnalyzeProgressSink;
10
+ telemetry?: TelemetrySink;
11
+ }
12
+ export interface ProjectConfig {
13
+ configFile: {
14
+ filename: string;
15
+ code: string;
16
+ };
17
+ packageJson: {
18
+ devDependencies: Record<string, string>;
19
+ scripts: Record<string, string>;
20
+ };
21
+ supportFiles: Array<{
22
+ filename: string;
23
+ code: string;
24
+ }>;
25
+ }
26
+ export interface ScaffoldResult {
27
+ url: string;
28
+ framework: Extract<AdapterType, 'cypress-e2e' | 'playwright'>;
29
+ generatedTests: GeneratedTest[];
30
+ scenarios: NeutralScenario[];
31
+ projectConfig: ProjectConfig;
32
+ }
33
+ export declare function scaffoldTests(url: string, options?: ScaffoldOptions): Promise<ScaffoldResult>;
34
+ //# sourceMappingURL=scaffold-tests.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,113 @@
1
+ import { analyzeApp } from './analyze.js';
2
+ import { createAdapter } from './adapters/adapter-factory.js';
3
+ function buildCypressProjectConfig(url) {
4
+ return {
5
+ configFile: {
6
+ filename: 'cypress.config.ts',
7
+ code: [
8
+ `import { defineConfig } from 'cypress';`,
9
+ ``,
10
+ `export default defineConfig({`,
11
+ ` e2e: {`,
12
+ ` baseUrl: ${JSON.stringify(url)},`,
13
+ ` viewportWidth: 1280,`,
14
+ ` viewportHeight: 720,`,
15
+ ` defaultCommandTimeout: 10000,`,
16
+ ` pageLoadTimeout: 30000,`,
17
+ ` video: false,`,
18
+ ` screenshotOnRunFailure: true,`,
19
+ ` screenshotsFolder: 'results/screenshots',`,
20
+ ` specPattern: 'cypress/e2e/**/*.cy.ts',`,
21
+ ` supportFile: 'cypress/support/e2e.ts',`,
22
+ ` },`,
23
+ `});`,
24
+ ``,
25
+ ].join('\n'),
26
+ },
27
+ packageJson: {
28
+ devDependencies: {
29
+ cypress: '^13.0.0',
30
+ typescript: '^5.4.0',
31
+ },
32
+ scripts: {
33
+ test: 'cypress run',
34
+ 'test:headed': 'cypress open',
35
+ 'test:ci': 'cypress run --reporter json --reporter-options output=results/cypress-results.json',
36
+ },
37
+ },
38
+ supportFiles: [
39
+ {
40
+ filename: 'cypress/support/e2e.ts',
41
+ code: [
42
+ `Cypress.on('uncaught:exception', () => false);`,
43
+ ``,
44
+ ].join('\n'),
45
+ },
46
+ ],
47
+ };
48
+ }
49
+ function buildPlaywrightProjectConfig(url) {
50
+ return {
51
+ configFile: {
52
+ filename: 'playwright.config.ts',
53
+ code: [
54
+ `import { defineConfig, devices } from '@playwright/test';`,
55
+ ``,
56
+ `export default defineConfig({`,
57
+ ` use: { baseURL: ${JSON.stringify(url)} },`,
58
+ ` projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],`,
59
+ `});`,
60
+ ``,
61
+ ].join('\n'),
62
+ },
63
+ packageJson: {
64
+ devDependencies: {
65
+ '@playwright/test': '^1.44.0',
66
+ typescript: '^5.4.0',
67
+ },
68
+ scripts: {
69
+ test: 'playwright test',
70
+ 'test:headed': 'playwright test --headed',
71
+ 'test:ci': 'playwright test --reporter=json',
72
+ },
73
+ },
74
+ supportFiles: [],
75
+ };
76
+ }
77
+ export async function scaffoldTests(url, options = {}) {
78
+ const framework = options.framework ?? 'cypress-e2e';
79
+ let scenarios;
80
+ if (options.scenarios && options.scenarios.length > 0) {
81
+ scenarios = options.scenarios;
82
+ }
83
+ else {
84
+ const result = await analyzeApp({
85
+ url,
86
+ config: {
87
+ maxPagesToScan: options.maxPagesToScan ?? 10,
88
+ maxDepth: 3,
89
+ minPagesForConfidence: 3,
90
+ timeoutMs: 30000,
91
+ retryCount: 0,
92
+ llmTokenBudget: 4096,
93
+ testGenerationLimit: 10,
94
+ enableLlmScenarios: true,
95
+ readOnlyMode: true,
96
+ requireHumanReview: false,
97
+ failOnConsoleError: false,
98
+ explorer: 'playwright',
99
+ defaultAdapter: framework,
100
+ adapters: [framework],
101
+ },
102
+ progressLog: options.progressLog,
103
+ telemetry: options.telemetry,
104
+ });
105
+ scenarios = result.gapAnalysis.scenarios;
106
+ }
107
+ const adapter = createAdapter(framework);
108
+ const generatedTests = adapter.renderAll(scenarios);
109
+ const projectConfig = framework === 'cypress-e2e'
110
+ ? buildCypressProjectConfig(url)
111
+ : buildPlaywrightProjectConfig(url);
112
+ return { url, framework, generatedTests, scenarios, projectConfig };
113
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qulib/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Qulib — analyze deployed web apps for honest quality gaps (CLI + programmatic API)",
5
5
  "license": "MIT",
6
6
  "author": "Tapesh Nagarwal",