@memberjunction/react-test-harness 5.20.0 → 5.21.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/dist/__tests__/component-linter/fixture-loader.d.ts +31 -0
- package/dist/__tests__/component-linter/fixture-loader.d.ts.map +1 -0
- package/dist/__tests__/component-linter/fixture-loader.js +129 -0
- package/dist/__tests__/component-linter/fixture-loader.js.map +1 -0
- package/dist/lib/component-linter.d.ts.map +1 -1
- package/dist/lib/component-linter.js +28 -7
- package/dist/lib/component-linter.js.map +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixture Loader for Component Linter Tests
|
|
3
|
+
*
|
|
4
|
+
* Loads component spec fixtures from JSON files for testing.
|
|
5
|
+
* Fixtures are organized into categories: broken-components, fixed-components, valid-components.
|
|
6
|
+
*
|
|
7
|
+
* Supports reference-based fixtures using $ref to point to source component specs
|
|
8
|
+
* in metadata/components/spec/. No database or metadata-sync dependency required.
|
|
9
|
+
*/
|
|
10
|
+
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
11
|
+
export interface FixtureMetadata {
|
|
12
|
+
name: string;
|
|
13
|
+
category: 'broken' | 'fixed' | 'valid';
|
|
14
|
+
description?: string;
|
|
15
|
+
expectedViolations?: string[];
|
|
16
|
+
isReference?: boolean;
|
|
17
|
+
sourcePath?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LoadedFixture {
|
|
20
|
+
metadata: FixtureMetadata;
|
|
21
|
+
spec: ComponentSpec;
|
|
22
|
+
filePath: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load a single fixture by category and name.
|
|
26
|
+
* Name can include nested subdirectory path (e.g. "schema-validation/entity-field-invalid").
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadFixture(category: 'broken' | 'fixed' | 'valid', name: string): Promise<LoadedFixture>;
|
|
29
|
+
/** Load all fixtures from a given category */
|
|
30
|
+
export declare function loadFixturesByCategory(category: 'broken' | 'fixed' | 'valid'): Promise<LoadedFixture[]>;
|
|
31
|
+
//# sourceMappingURL=fixture-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-loader.d.ts","sourceRoot":"","sources":["../../../src/__tests__/component-linter/fixture-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAc5E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAoDD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2C9G;AAED,8CAA8C;AAC9C,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAsB7G"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixture Loader for Component Linter Tests
|
|
3
|
+
*
|
|
4
|
+
* Loads component spec fixtures from JSON files for testing.
|
|
5
|
+
* Fixtures are organized into categories: broken-components, fixed-components, valid-components.
|
|
6
|
+
*
|
|
7
|
+
* Supports reference-based fixtures using $ref to point to source component specs
|
|
8
|
+
* in metadata/components/spec/. No database or metadata-sync dependency required.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
/** Root of the fixtures directory relative to this file */
|
|
13
|
+
const FIXTURES_ROOT = path.resolve(__dirname, '../../../fixtures/component-linter');
|
|
14
|
+
/** Root of the MJ repository (for resolving $ref paths in valid-component fixtures) */
|
|
15
|
+
const REPO_ROOT = path.resolve(__dirname, '../../../../../..');
|
|
16
|
+
function isReferenceFixture(data) {
|
|
17
|
+
return data !== null && typeof data === 'object' && '$ref' in data && typeof data.$ref === 'string';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve @file: references in a JSON object.
|
|
21
|
+
* Replaces string values like "@file:./path/to/code.js" with the file contents.
|
|
22
|
+
*/
|
|
23
|
+
function resolveFileReferences(obj, basePath) {
|
|
24
|
+
const result = {};
|
|
25
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
26
|
+
if (typeof value === 'string' && value.startsWith('@file:')) {
|
|
27
|
+
const filePath = path.resolve(path.dirname(basePath), value.slice('@file:'.length));
|
|
28
|
+
try {
|
|
29
|
+
result[key] = fs.readFileSync(filePath, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
result[key] = value; // Keep original if file not found
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (Array.isArray(value)) {
|
|
36
|
+
result[key] = value.map((item) => item !== null && typeof item === 'object' && !Array.isArray(item) ? resolveFileReferences(item, basePath) : item);
|
|
37
|
+
}
|
|
38
|
+
else if (value !== null && typeof value === 'object') {
|
|
39
|
+
result[key] = resolveFileReferences(value, basePath);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
result[key] = value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
/** Recursively collect all .json file paths from a directory */
|
|
48
|
+
async function getJsonFilesRecursive(dirPath, baseDir = dirPath) {
|
|
49
|
+
const results = [];
|
|
50
|
+
try {
|
|
51
|
+
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
results.push(...(await getJsonFilesRecursive(fullPath, baseDir)));
|
|
56
|
+
}
|
|
57
|
+
else if (entry.name.endsWith('.json')) {
|
|
58
|
+
results.push(path.relative(baseDir, fullPath));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Directory doesn't exist
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Load a single fixture by category and name.
|
|
69
|
+
* Name can include nested subdirectory path (e.g. "schema-validation/entity-field-invalid").
|
|
70
|
+
*/
|
|
71
|
+
export async function loadFixture(category, name) {
|
|
72
|
+
const categoryDir = `${category}-components`;
|
|
73
|
+
const nameWithExt = name.endsWith('.json') ? name : `${name}.json`;
|
|
74
|
+
const filePath = path.join(FIXTURES_ROOT, categoryDir, nameWithExt);
|
|
75
|
+
if (!fs.existsSync(filePath)) {
|
|
76
|
+
throw new Error(`Fixture not found: ${filePath}`);
|
|
77
|
+
}
|
|
78
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
79
|
+
const rawData = JSON.parse(content);
|
|
80
|
+
let spec;
|
|
81
|
+
let isReference = false;
|
|
82
|
+
let sourcePath;
|
|
83
|
+
let descriptionOverride;
|
|
84
|
+
if (isReferenceFixture(rawData)) {
|
|
85
|
+
isReference = true;
|
|
86
|
+
descriptionOverride = rawData.description;
|
|
87
|
+
sourcePath = path.resolve(REPO_ROOT, rawData.$ref);
|
|
88
|
+
if (!fs.existsSync(sourcePath)) {
|
|
89
|
+
throw new Error(`Referenced spec not found: ${sourcePath} (from $ref: ${rawData.$ref})`);
|
|
90
|
+
}
|
|
91
|
+
const specContent = await fs.promises.readFile(sourcePath, 'utf8');
|
|
92
|
+
const specData = JSON.parse(specContent);
|
|
93
|
+
spec = resolveFileReferences(specData, sourcePath);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
spec = resolveFileReferences(rawData, filePath);
|
|
97
|
+
}
|
|
98
|
+
const metadata = {
|
|
99
|
+
name,
|
|
100
|
+
category,
|
|
101
|
+
description: descriptionOverride || spec.description,
|
|
102
|
+
expectedViolations: isReferenceFixture(rawData) ? rawData.expectedViolations : undefined,
|
|
103
|
+
isReference,
|
|
104
|
+
sourcePath,
|
|
105
|
+
};
|
|
106
|
+
return { metadata, spec, filePath };
|
|
107
|
+
}
|
|
108
|
+
/** Load all fixtures from a given category */
|
|
109
|
+
export async function loadFixturesByCategory(category) {
|
|
110
|
+
const categoryDir = `${category}-components`;
|
|
111
|
+
const dirPath = path.join(FIXTURES_ROOT, categoryDir);
|
|
112
|
+
if (!fs.existsSync(dirPath)) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const jsonFiles = await getJsonFilesRecursive(dirPath);
|
|
116
|
+
const fixtures = [];
|
|
117
|
+
for (const relativePath of jsonFiles) {
|
|
118
|
+
const name = relativePath.replace(/\.json$/, '');
|
|
119
|
+
try {
|
|
120
|
+
fixtures.push(await loadFixture(category, name));
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
// Record load failure but don't block other fixtures
|
|
124
|
+
console.warn(`Warning: Failed to load fixture ${category}/${name}: ${err instanceof Error ? err.message : err}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return fixtures;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=fixture-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-loader.js","sourceRoot":"","sources":["../../../src/__tests__/component-linter/fixture-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-linter.d.ts","sourceRoot":"","sources":["../../src/lib/component-linter.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAwD,MAAM,6CAA6C,CAAC;AAIlI,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAU/D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,cAAc,CAAC;IACnF,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AA0OD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,cAAc,CAAqB;IAGlD,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAQhC,OAAO,CAAC,MAAM,CAAC,cAAc;IA8B7B,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAyC3C,OAAO,CAAC,MAAM,CAAC,uBAAuB,
|
|
1
|
+
{"version":3,"file":"component-linter.d.ts","sourceRoot":"","sources":["../../src/lib/component-linter.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAwD,MAAM,6CAA6C,CAAC;AAIlI,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAU/D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,cAAc,CAAC;IACnF,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AA0OD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,cAAc,CAAqB;IAGlD,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAQhC,OAAO,CAAC,MAAM,CAAC,cAAc;IA8B7B,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAyC3C,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAk4PpC;WAEkB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;WAmC3G,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,aAAa,CAAC,EAAE,aAAa,EAC7B,eAAe,CAAC,EAAE,OAAO,EACzB,WAAW,CAAC,EAAE,QAAQ,EACtB,SAAS,CAAC,EAAE,OAAO,EACnB,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,UAAU,CAAC;IA4KtB,OAAO,CAAC,MAAM,CAAC,wBAAwB;IA+avC,OAAO,CAAC,MAAM,CAAC,eAAe;IA2B9B,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAyBpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,0BAA0B;IA4hCzC,OAAO,CAAC,MAAM,CAAC,8BAA8B;IA0B7C;;OAEG;mBACkB,qBAAqB;IA0H1C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,0BAA0B;IAqDzC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,6BAA6B;IAwB5C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IA+BlC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IA+KpC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IA2DlC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;CAqGzC"}
|
|
@@ -7216,9 +7216,30 @@ const extendedCallbacks = { ...callbacks, onCustomEvent: handler };
|
|
|
7216
7216
|
// No queries defined, so no violation
|
|
7217
7217
|
return violations;
|
|
7218
7218
|
}
|
|
7219
|
-
|
|
7219
|
+
const allQueryNames = componentSpec.dataRequirements.queries.map((q) => q.name).filter(Boolean);
|
|
7220
|
+
// In hierarchical components, the root's dataRequirements contains the complete
|
|
7221
|
+
// set of queries for the entire tree, but child components can own subsets and
|
|
7222
|
+
// call RunQuery themselves. Collect query names claimed by child dependencies
|
|
7223
|
+
// so we only require the root to call RunQuery for unclaimed queries.
|
|
7224
|
+
const childClaimedQueries = new Set();
|
|
7225
|
+
if (componentSpec.dependencies) {
|
|
7226
|
+
for (const dep of componentSpec.dependencies) {
|
|
7227
|
+
if (dep.dataRequirements?.queries) {
|
|
7228
|
+
for (const q of dep.dataRequirements.queries) {
|
|
7229
|
+
if (q.name) {
|
|
7230
|
+
childClaimedQueries.add(q.name);
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
const unclaimedQueryNames = allQueryNames.filter((name) => !childClaimedQueries.has(name));
|
|
7237
|
+
// If all queries are delegated to child components, no violation for the root
|
|
7238
|
+
if (unclaimedQueryNames.length === 0) {
|
|
7239
|
+
return violations;
|
|
7240
|
+
}
|
|
7241
|
+
// Track whether RunQuery is called anywhere in the root component's code
|
|
7220
7242
|
let hasRunQueryCall = false;
|
|
7221
|
-
const queryNames = componentSpec.dataRequirements.queries.map((q) => q.name).filter(Boolean);
|
|
7222
7243
|
traverse(ast, {
|
|
7223
7244
|
CallExpression(path) {
|
|
7224
7245
|
// Check for utilities.rq.RunQuery pattern
|
|
@@ -7242,27 +7263,27 @@ const extendedCallbacks = { ...callbacks, onCustomEvent: handler };
|
|
|
7242
7263
|
}
|
|
7243
7264
|
},
|
|
7244
7265
|
});
|
|
7245
|
-
// If queries
|
|
7266
|
+
// If unclaimed queries exist but RunQuery is never called, that's a critical violation
|
|
7246
7267
|
if (!hasRunQueryCall) {
|
|
7247
7268
|
violations.push({
|
|
7248
7269
|
rule: 'required-queries-not-called',
|
|
7249
7270
|
severity: 'critical',
|
|
7250
7271
|
line: 1,
|
|
7251
7272
|
column: 0,
|
|
7252
|
-
message: `Component has ${
|
|
7273
|
+
message: `Component has ${unclaimedQueryNames.length} defined ${unclaimedQueryNames.length === 1 ? 'query' : 'queries'} in dataRequirements (mode: '${mode}') but never calls RunQuery. Queries defined: ${unclaimedQueryNames.join(', ')}`,
|
|
7253
7274
|
suggestion: {
|
|
7254
7275
|
text: `When dataRequirements.mode is '${mode}' and includes queries, you must use utilities.rq.RunQuery to execute them, not RunView.`,
|
|
7255
|
-
example: `// Your dataRequirements defines these queries: ${
|
|
7276
|
+
example: `// Your dataRequirements defines these queries: ${unclaimedQueryNames.join(', ')}
|
|
7256
7277
|
// Mode is set to: '${mode}'
|
|
7257
7278
|
|
|
7258
7279
|
// ❌ WRONG - Using RunView for a query:
|
|
7259
7280
|
const result = await utilities.rv.RunView({
|
|
7260
|
-
EntityName: '${
|
|
7281
|
+
EntityName: '${unclaimedQueryNames[0] || 'QueryName'}'
|
|
7261
7282
|
});
|
|
7262
7283
|
|
|
7263
7284
|
// ✅ CORRECT - Using RunQuery for queries:
|
|
7264
7285
|
const result = await utilities.rq.RunQuery({
|
|
7265
|
-
QueryName: '${
|
|
7286
|
+
QueryName: '${unclaimedQueryNames[0] || 'QueryName'}'
|
|
7266
7287
|
});
|
|
7267
7288
|
|
|
7268
7289
|
// Key differences:
|